Merge "Add layerStack field to DisplayInfo"
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index cc2a6f7..e7d0ad0 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -10,9 +10,7 @@
 
     shared_libs: [
         "libbinder",
-        "libhwbinder",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
         "libcutils",
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 93bbe90..09aee89 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -88,7 +88,6 @@
         "libdumputils",
         "libhardware_legacy",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
     ],
@@ -119,6 +118,8 @@
         "kill",
         "librank",
         "logcat",
+        "lpdump",
+        "lpdumpd",
         "lsmod",
         "lsof",
         "netstat",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b781da3..86558ef 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -929,6 +929,14 @@
     RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
+static void DumpDynamicPartitionInfo() {
+    if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+        return;
+    }
+
+    RunCommand("LPDUMP", {"lpdump", "--all"});
+}
+
 static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) {
     MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path,
            anr_traces_dir.c_str());
@@ -1515,6 +1523,7 @@
     }
     add_mountinfo();
     DumpIpTablesAsRoot();
+    DumpDynamicPartitionInfo();
 
     // Capture any IPSec policies in play. No keys are exposed here.
     RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build());
@@ -1982,7 +1991,7 @@
 
 static void Vibrate(int duration_ms) {
     // clang-format off
-    RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"},
+    RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"},
                CommandOptions::WithTimeout(10)
                    .Log("Vibrate: '%s'\n")
                    .Always()
@@ -3496,7 +3505,7 @@
 
     struct sockaddr addr;
     socklen_t alen = sizeof(addr);
-    int fd = accept(s, &addr, &alen);
+    int fd = accept4(s, &addr, &alen, SOCK_CLOEXEC);
 
     // Close socket just after accept(), to make sure that connect() by client will get error
     // when the socket is used by the other services.
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
new file mode 100644
index 0000000..40b8dc7
--- /dev/null
+++ b/cmds/idlcli/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "idlcli-defaults",
+    shared_libs: [
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"idlcli\"",
+    ],
+}
+
+cc_library {
+    name: "libidlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: [
+        "CommandVibrator.cpp",
+        "vibrator/CommandOff.cpp",
+        "vibrator/CommandOn.cpp",
+        "vibrator/CommandPerform.cpp",
+        "vibrator/CommandSetAmplitude.cpp",
+        "vibrator/CommandSetExternalControl.cpp",
+        "vibrator/CommandSupportsAmplitudeControl.cpp",
+        "vibrator/CommandSupportsExternalControl.cpp",
+    ],
+    visibility: [":__subpackages__"],
+}
+
+cc_binary {
+    name: "idlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: ["main.cpp"],
+    whole_static_libs: ["libidlcli"],
+}
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
new file mode 100644
index 0000000..a7a70c3
--- /dev/null
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli;
+
+class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
+    std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<api> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<api>", CommandRegistry<CommandVibrator>::List()},
+        };
+        return details;
+    }
+};
+
+static const auto Command = CommandRegistry<IdlCli>::Register<CommandVibrator>("vibrator");
+
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
new file mode 100644
index 0000000..dd84304
--- /dev/null
+++ b/cmds/idlcli/IdlCli.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+
+#include "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli : public CommandWithSubcommands<IdlCli> {
+    std::string getDescription() const override { return "Invoke IDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<idl>", CommandRegistry<IdlCli>::List()},
+        };
+        return details;
+    }
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
new file mode 100644
index 0000000..9ed9d82
--- /dev/null
+++ b/cmds/idlcli/main.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IdlCli.h"
+#include "utils.h"
+
+int main(const int argc, const char* const argv[]) {
+    using namespace ::android::idlcli;
+    return IdlCli{}.main(Args{argc, argv});
+}
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
new file mode 100644
index 0000000..a8e5954
--- /dev/null
+++ b/cmds/idlcli/utils.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+
+#include <hidl/HidlSupport.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idlcli {
+
+namespace overrides {
+
+namespace details {
+
+template <typename T>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    auto pos = stream.tellg();
+    auto tmp = +out;
+    auto min = +std::numeric_limits<T>::min();
+    auto max = +std::numeric_limits<T>::max();
+    stream >> tmp;
+    if (!stream) {
+        return stream;
+    }
+    if (tmp < min || tmp > max) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+} // namespace details
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, int8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+} // namespace overrides
+
+template <typename T, typename R = hardware::hidl_enum_range<T>>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    using overrides::operator>>;
+    auto validRange = R();
+    auto pos = stream.tellg();
+    std::underlying_type_t<T> in;
+    T tmp;
+    stream >> in;
+    if (!stream) {
+        return stream;
+    }
+    tmp = static_cast<T>(in);
+    if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+enum Status : unsigned int {
+    OK,
+    USAGE,
+    UNAVAILABLE,
+    ERROR,
+};
+
+class Args {
+public:
+    Args(const int argc, const char *const argv[]) {
+        for (int argi = 0; argi < argc; argi++) {
+            mArgs.emplace_back(std::string_view(argv[argi]));
+        }
+    }
+
+    template <typename T = std::string>
+    std::optional<T> get() {
+        return get<T>(false);
+    }
+
+    template <typename T = std::string>
+    std::optional<T> pop() {
+        return get<T>(true);
+    }
+
+    bool empty() { return mArgs.empty(); }
+
+private:
+    template <typename T>
+    std::optional<T> get(bool erase) {
+        using idlcli::operator>>;
+        using overrides::operator>>;
+        T retValue;
+
+        if (mArgs.empty()) {
+            return {};
+        }
+
+        std::stringstream stream{std::string{mArgs.front()}};
+        stream >> std::setbase(0) >> retValue;
+        if (!stream || !stream.eof()) {
+            return {};
+        }
+
+        if (erase) {
+            mArgs.erase(mArgs.begin());
+        }
+
+        return retValue;
+    }
+
+    std::vector<std::string_view> mArgs;
+};
+
+class Command {
+protected:
+    struct Usage {
+        std::string name;
+        std::vector<std::string> details;
+    };
+    using UsageDetails = std::vector<Usage>;
+
+public:
+    virtual ~Command() = default;
+
+    Status main(Args &&args) {
+        Status status = doArgsAndMain(std::move(args));
+        if (status == USAGE) {
+            printUsage();
+            return ERROR;
+        }
+        if (status == UNAVAILABLE) {
+            std::cerr << "The requested operation is unavailable." << std::endl;
+            return ERROR;
+        }
+        return status;
+    }
+
+private:
+    virtual std::string getDescription() const = 0;
+    virtual std::string getUsageSummary() const = 0;
+    virtual UsageDetails getUsageDetails() const = 0;
+    virtual Status doArgs(Args &args) = 0;
+    virtual Status doMain(Args &&args) = 0;
+
+    void printUsage() const {
+        std::cerr << "Description:\n  " << getDescription() << std::endl;
+        std::cerr << "Usage:\n  " << mName << " " << getUsageSummary() << std::endl;
+
+        std::cerr << "Details:" << std::endl;
+        size_t entryNameWidth = 0;
+        for (auto &entry : getUsageDetails()) {
+            entryNameWidth = std::max(entryNameWidth, entry.name.length());
+        }
+        for (auto &entry : getUsageDetails()) {
+            auto prefix = entry.name;
+            for (auto &line : entry.details) {
+                std::cerr << "  " << std::left << std::setw(entryNameWidth + 8) << prefix << line
+                          << std::endl;
+                prefix = "";
+            }
+        }
+    }
+
+    Status doArgsAndMain(Args &&args) {
+        Status status;
+        mName = *args.pop();
+        if ((status = doArgs(args)) != OK) {
+            return status;
+        }
+        if ((status = doMain(std::move(args))) != OK) {
+            return status;
+        }
+        return OK;
+    }
+
+protected:
+    std::string mName;
+};
+
+template <typename T>
+class CommandRegistry {
+private:
+    using CommandCreator = std::function<std::unique_ptr<Command>()>;
+
+public:
+    template <typename U>
+    static CommandCreator Register(const std::string name) {
+        Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
+        return Instance()->mCommands[name];
+    }
+
+    static std::unique_ptr<Command> Create(const std::string name) {
+        auto it = Instance()->mCommands.find(name);
+        if (it == Instance()->mCommands.end()) {
+            return nullptr;
+        }
+        return it->second();
+    }
+
+    static auto List() {
+        std::vector<std::string> list;
+        for (auto &it : Instance()->mCommands) {
+            list.push_back(it.first);
+        }
+        std::sort(list.begin(), list.end());
+        return list;
+    }
+
+private:
+    static CommandRegistry *Instance() {
+        static CommandRegistry sRegistry;
+        return &sRegistry;
+    }
+
+private:
+    std::map<const std::string, CommandCreator> mCommands;
+};
+
+template <typename T>
+class CommandWithSubcommands : public Command {
+private:
+    Status doArgs(Args &args) override {
+        mCommand = CommandRegistry<T>::Create(*args.get());
+        if (!mCommand) {
+            std::cerr << "Invalid Command!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }
+
+protected:
+    std::unique_ptr<Command> mCommand;
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
new file mode 100644
index 0000000..bcb207b
--- /dev/null
+++ b/cmds/idlcli/vibrator.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+
+#include "utils.h"
+
+#include "log/log.h"
+
+namespace android {
+
+using hardware::Return;
+
+static constexpr int NUM_TRIES = 2;
+
+// Creates a Return<R> with STATUS::EX_NULL_POINTER.
+template <class R>
+inline Return<R> NullptrStatus() {
+    using ::android::hardware::Status;
+    return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+}
+
+template <typename I>
+class HalWrapper {
+public:
+    static std::unique_ptr<HalWrapper> Create() {
+        // Assume that if getService returns a nullptr, HAL is not available on the
+        // device.
+        auto hal = I::getService();
+        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
+    }
+
+    template <class R, class... Args0, class... Args1>
+    Return<R> call(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+        return (*mHal.*fn)(std::forward<Args1>(args1)...);
+    }
+
+private:
+    HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+
+private:
+    sp<I> mHal;
+};
+
+template <typename I>
+static auto getHal() {
+    static auto sHalWrapper = HalWrapper<I>::Create();
+    return sHalWrapper.get();
+}
+
+template <class R, class I, class... Args0, class... Args1>
+Return<R> halCall(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+    auto hal = getHal<I>();
+    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
+}
+
+namespace idlcli {
+namespace vibrator {
+
+namespace V1_0 = ::android::hardware::vibrator::V1_0;
+namespace V1_1 = ::android::hardware::vibrator::V1_1;
+namespace V1_2 = ::android::hardware::vibrator::V1_2;
+namespace V1_3 = ::android::hardware::vibrator::V1_3;
+
+} // namespace vibrator
+} // namespace idlcli
+
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
new file mode 100644
index 0000000..a674f01
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOff : public Command {
+    std::string getDescription() const override { return "Turn off vibrator."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::off);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOff>("off");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
new file mode 100644
index 0000000..2164b7d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOn : public Command {
+    std::string getDescription() const override { return "Turn on vibrator."; }
+
+    std::string getUsageSummary() const override { return "<duration>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<duration>", {"In milliseconds."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto duration = args.pop<decltype(mDuration)>()) {
+            mDuration = *duration;
+        } else {
+            std::cerr << "Missing or Invalid Duration!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::on, mDuration);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint32_t mDuration;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOn>("on");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
new file mode 100644
index 0000000..688cbd8
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using V1_0::EffectStrength;
+using V1_3::Effect;
+
+class CommandPerform : public Command {
+    std::string getDescription() const override { return "Perform vibration effect."; }
+
+    std::string getUsageSummary() const override { return "<effect> <strength>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<effect>", {"Effect ID."}},
+                {"<strength>", {"0-2."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto effect = args.pop<decltype(mEffect)>()) {
+            mEffect = *effect;
+            std::cout << "Effect: " << toString(mEffect) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Effect!" << std::endl;
+            return USAGE;
+        }
+        if (auto strength = args.pop<decltype(mStrength)>()) {
+            mStrength = *strength;
+            std::cout << "Strength: " << toString(mStrength) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Strength!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        Return<void> ret;
+        V1_0::Status status;
+        uint32_t lengthMs;
+        auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+            status = retStatus;
+            lengthMs = retLengthMs;
+        };
+
+        if (auto hal = getHal<V1_3::IVibrator>()) {
+            ret = hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_2::IVibrator>()) {
+            ret = hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_1::IVibrator>()) {
+            ret = hal->call(&V1_1::IVibrator::perform_1_1, static_cast<V1_1::Effect_1_1>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            ret = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
+                            mStrength, callback);
+        } else {
+            ret = NullptrStatus<void>();
+        }
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(status) << std::endl;
+        std::cout << "Length: " << lengthMs << std::endl;
+
+        return status == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    Effect mEffect;
+    EffectStrength mStrength;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerform>("perform");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
new file mode 100644
index 0000000..38a1dc2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetAmplitude : public Command {
+    std::string getDescription() const override { return "Set vibration amplitude."; }
+
+    std::string getUsageSummary() const override { return "<amplitude>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<amplitude>", {"1-255."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto amplitude = args.pop<decltype(mAmplitude)>()) {
+            mAmplitude = *amplitude;
+        } else {
+            std::cerr << "Missing or Invalid Amplitude!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::setAmplitude, mAmplitude);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint8_t mAmplitude;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetAmplitude>("setAmplitude");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
new file mode 100644
index 0000000..5fb1fac
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetExternalControl : public Command {
+    std::string getDescription() const override {
+        return "Enable/disable vibration external control.";
+    }
+
+    std::string getUsageSummary() const override { return "<enable>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<enable>", {"0/1."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto enable = args.pop<decltype(mEnable)>()) {
+            mEnable = *enable;
+        } else {
+            std::cerr << "Missing Enable!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::setExternalControl, mEnable);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    bool mEnable;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetExternalControl>("setExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
new file mode 100644
index 0000000..cdc529a
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsAmplitudeControl : public Command {
+    std::string getDescription() const override { return "Check support for amplitude control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsAmplitudeControl>(
+                "supportsAmplitudeControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
new file mode 100644
index 0000000..ed15d76
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsExternalControl : public Command {
+    std::string getDescription() const override { return "Check support for external control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::supportsExternalControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsExternalControl>(
+                "supportsExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index f8b8c68..5afae4b 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -19,7 +19,6 @@
         "libcutils",
         "libutils",
         "libhidlbase",
-        "libhidltransport",
         "libhidl-gen-hash",
         "libhidl-gen-utils",
         "libvintf",
@@ -51,7 +50,6 @@
         "libcutils",
         "libutils",
         "libhidlbase",
-        "libhidltransport",
         "libhidl-gen-hash",
         "libhidl-gen-utils",
         "libvintf",
@@ -81,9 +79,7 @@
         "libgmock",
     ],
     shared_libs: [
-        "libhwbinder",
         "libhidlbase",
-        "libhidltransport",
         "libvintf",
     ],
     srcs: [
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index ad7e4c4..a7ccf64 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -163,11 +163,11 @@
 VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object,
                        const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) {
     bool found = false;
-    (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(),
-                                           [&](const auto& instance) {
-                                               found = match(instance, fqInstance, ta);
-                                               return !found; // continue if not found
-                                           });
+    (void)object->forEachHidlInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(),
+                                               [&](const auto& instance) {
+                                                   found = match(instance, fqInstance, ta);
+                                                   return !found; // continue if not found
+                                               });
     return found ? value : VINTF_INFO_EMPTY;
 }
 
@@ -453,7 +453,7 @@
     }
 
     bool found = false;
-    (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) {
+    (void)manifest->forEachHidlInstanceOfVersion(package, version, [&found](const auto&) {
         found = true;
         return false; // break
     });
@@ -797,9 +797,9 @@
 
         std::map<std::string, TableEntry> entries;
 
-        manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) {
+        manifest->forEachHidlInstance([&] (const vintf::ManifestInstance& manifestInstance) {
             TableEntry entry{
-                .interfaceName = manifestInstance.getFqInstance().string(),
+                .interfaceName = manifestInstance.description(),
                 .transport = manifestInstance.transport(),
                 .arch = manifestInstance.arch(),
                 // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM.
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 76f7c7f..3d550ba 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -452,7 +452,7 @@
 }
 
 TEST_F(ListTest, DumpVintf) {
-    const std::string expected = "<manifest version=\"1.0\" type=\"device\">\n"
+    const std::string expected = "<manifest version=\"2.0\" type=\"device\">\n"
                                  "    <hal format=\"hidl\">\n"
                                  "        <name>a.h.foo1</name>\n"
                                  "        <transport>hwbinder</transport>\n"
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 9cf3c5c..7277e85 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -15,11 +15,18 @@
     shared_libs: [
         "libbase",
         "libbinder", // also contains servicemanager_interface
+        "libvintf",
         "libcutils",
         "liblog",
         "libutils",
         "libselinux",
     ],
+
+    target: {
+        vendor: {
+            exclude_shared_libs: ["libvintf"],
+        },
+    },
 }
 
 cc_binary {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index b3aa342..9344368 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -17,13 +17,53 @@
 #include "ServiceManager.h"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <binder/Stability.h>
 #include <cutils/android_filesystem_config.h>
 #include <cutils/multiuser.h>
+#include <thread>
+
+#ifndef VENDORSERVICEMANAGER
+#include <vintf/VintfObject.h>
+#include <vintf/constants.h>
+#endif  // !VENDORSERVICEMANAGER
 
 using ::android::binder::Status;
+using ::android::internal::Stability;
 
 namespace android {
 
+#ifndef VENDORSERVICEMANAGER
+static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
+    if (!Stability::requiresVintfDeclaration(binder)) {
+        return true;
+    }
+
+    size_t firstSlash = name.find('/');
+    size_t lastDot = name.rfind('.', firstSlash);
+    if (firstSlash == std::string::npos || lastDot == std::string::npos) {
+        LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
+                   << "some.package.foo.IFoo/default) but got: " << name;
+        return false;
+    }
+    const std::string package = name.substr(0, lastDot);
+    const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
+    const std::string instance = name.substr(firstSlash+1);
+
+    for (const auto& manifest : {
+            vintf::VintfObject::GetDeviceHalManifest(),
+            vintf::VintfObject::GetFrameworkHalManifest()
+        }) {
+        if (manifest->hasAidlInstance(package, iface, instance)) {
+            return true;
+        }
+    }
+    LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+               << " in the VINTF manifest.";
+    return false;
+}
+#endif  // !VENDORSERVICEMANAGER
+
 ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}
 ServiceManager::~ServiceManager() {
     // this should only happen in tests
@@ -41,39 +81,44 @@
 }
 
 Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
-    // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
-    return checkService(name, outBinder);
+    *outBinder = tryGetService(name, true);
+    // returns ok regardless of result for legacy reasons
+    return Status::ok();
 }
 
 Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
+    *outBinder = tryGetService(name, false);
+    // returns ok regardless of result for legacy reasons
+    return Status::ok();
+}
+
+sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
     auto ctx = mAccess->getCallingContext();
 
-    auto it = mNameToService.find(name);
-    if (it == mNameToService.end()) {
-        *outBinder = nullptr;
-        return Status::ok();
-    }
+    sp<IBinder> out;
+    if (auto it = mNameToService.find(name); it != mNameToService.end()) {
+        const Service& service = it->second;
 
-    const Service& service = it->second;
+        if (!service.allowIsolated) {
+            uid_t appid = multiuser_get_app_id(ctx.uid);
+            bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
 
-    if (!service.allowIsolated) {
-        uid_t appid = multiuser_get_app_id(ctx.uid);
-        bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
-
-        if (isIsolated) {
-            *outBinder = nullptr;
-            return Status::ok();
+            if (isIsolated) {
+                return nullptr;
+            }
         }
+        out = service.binder;
     }
 
     if (!mAccess->canFind(ctx, name)) {
-        // returns ok and null for legacy reasons
-        *outBinder = nullptr;
-        return Status::ok();
+        return nullptr;
     }
 
-    *outBinder = service.binder;
-    return Status::ok();
+    if (!out && startIfNotFound) {
+        tryStartService(name);
+    }
+
+    return out;
 }
 
 bool isValidServiceName(const std::string& name) {
@@ -112,6 +157,13 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
+#ifndef VENDORSERVICEMANAGER
+    if (!meetsDeclarationRequirements(binder, name)) {
+        // already logged
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+    }
+#endif  // !VENDORSERVICEMANAGER
+
     // implicitly unlinked when the binder is removed
     if (OK != binder->linkToDeath(this)) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
@@ -253,4 +305,13 @@
     }
 }
 
+void ServiceManager::tryStartService(const std::string& name) {
+    ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service",
+          name.c_str());
+
+    std::thread([=] {
+        (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+    }).detach();
+}
+
 }  // namespace android
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index fcc5124..006e519 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -30,6 +30,7 @@
     ServiceManager(std::unique_ptr<Access>&& access);
     ~ServiceManager();
 
+    // getService will try to start any services it cannot find
     binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
     binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override;
     binder::Status addService(const std::string& name, const sp<IBinder>& binder,
@@ -42,6 +43,9 @@
 
     void binderDied(const wp<IBinder>& who) override;
 
+protected:
+    virtual void tryStartService(const std::string& name);
+
 private:
     struct Service {
         sp<IBinder> binder; // not null
@@ -57,6 +61,7 @@
     void removeCallback(const wp<IBinder>& who,
                         CallbackMap::iterator* it,
                         bool* found);
+    sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
 
     CallbackMap mNameToCallback;
     ServiceMap mNameToService;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 9f6193b..11d43a6 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -36,8 +36,6 @@
 
     const char* driver = argc == 2 ? argv[1] : "/dev/binder";
 
-    android::base::InitLogging(nullptr, &android::base::KernelLogger);
-
     sp<ProcessState> ps = ProcessState::initWithDriver(driver);
     ps->setThreadPoolMaxThreadCount(0);
     ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 3c211d2..25245be 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -57,6 +57,12 @@
     MOCK_METHOD1(canList, bool(const CallingContext&));
 };
 
+class MockServiceManager : public ServiceManager {
+ public:
+    MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {}
+    MOCK_METHOD1(tryStartService, void(const std::string& name));
+};
+
 static sp<ServiceManager> getPermissiveServiceManager() {
     std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
 
@@ -65,7 +71,7 @@
     ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
     ON_CALL(*access, canList(_)).WillByDefault(Return(true));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
     return sm;
 }
 
@@ -113,7 +119,7 @@
             .uid = uid,
         }));
         EXPECT_CALL(*access, canAdd(_, _)).Times(0);
-        sp<ServiceManager> sm = new ServiceManager(std::move(access));
+        sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
         EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
             IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -135,7 +141,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
     EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -168,7 +174,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -192,7 +198,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
     sp<IBinder> service = getBinder();
     EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -218,7 +224,7 @@
     // TODO(b/136023468): when security check is first, this should be called first
     // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -235,7 +241,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
 
     std::vector<std::string> out;
     EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 59d16d1..eaa562b 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -359,6 +359,9 @@
     DEFINE_AXIS(BRAKE),
     DEFINE_AXIS(DISTANCE),
     DEFINE_AXIS(TILT),
+    DEFINE_AXIS(SCROLL),
+    DEFINE_AXIS(RELATIVE_X),
+    DEFINE_AXIS(RELATIVE_Y),
     DEFINE_AXIS(GENERIC_1),
     DEFINE_AXIS(GENERIC_2),
     DEFINE_AXIS(GENERIC_3),
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 9284acb..2d6292c 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -34,6 +34,7 @@
     name: "libandroid_runtime_lazy",
     vendor_available: true,
     double_loadable: true,
+    host_supported: true,
 
     cflags: [
         "-Wall",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6ece4a2..f3d4b7e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -16,6 +16,8 @@
     name: "libbinder_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    host_supported: true,
+
     header_libs: [
         "libbase_headers",
         "libcutils_headers",
@@ -28,6 +30,27 @@
     ],
 }
 
+// These interfaces are android-specific implementation unrelated to binder
+// transport itself and should be moved to AIDL or in domain-specific libs.
+//
+// Currently, these are only on system android (not vendor, not host)
+libbinder_device_interface_sources = [
+    "ActivityManager.cpp",
+    "AppOpsManager.cpp",
+    "IActivityManager.cpp",
+    "IAppOpsCallback.cpp",
+    "IAppOpsService.cpp",
+    "IBatteryStats.cpp",
+    "IMediaResourceMonitor.cpp",
+    "IPermissionController.cpp",
+    "IProcessInfoService.cpp",
+    "IUidObserver.cpp",
+    "PermissionCache.cpp",
+    "PermissionController.cpp",
+    "ProcessInfoService.cpp",
+    "IpPrefix.cpp",
+]
+
 cc_library_shared {
     name: "libbinder",
 
@@ -37,6 +60,13 @@
         enabled: true,
     },
     double_loadable: true,
+    host_supported: true,
+
+    // TODO(b/31559095): get headers from bionic on host
+    include_dirs: [
+        "bionic/libc/kernel/android/uapi/",
+        "bionic/libc/kernel/uapi/",
+    ],
 
     // libbinder does not offer a stable wire protocol.
     // if a second copy of it is installed, then it may break after security
@@ -44,60 +74,36 @@
     no_apex: true,
 
     srcs: [
-        "ActivityManager.cpp",
-        "AppOpsManager.cpp",
         "Binder.cpp",
         "BpBinder.cpp",
         "BufferedTextOutput.cpp",
         "Debug.cpp",
-        "IActivityManager.cpp",
-        "IAppOpsCallback.cpp",
-        "IAppOpsService.cpp",
-        "IBatteryStats.cpp",
         "IInterface.cpp",
-        "IMediaResourceMonitor.cpp",
         "IMemory.cpp",
         "IPCThreadState.cpp",
-        "IPermissionController.cpp",
-        "IProcessInfoService.cpp",
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
         "IShellCallback.cpp",
-        "IUidObserver.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
         "Parcel.cpp",
         "ParcelFileDescriptor.cpp",
-        "PermissionCache.cpp",
-        "PermissionController.cpp",
         "PersistableBundle.cpp",
-        "ProcessInfoService.cpp",
         "ProcessState.cpp",
         "Static.cpp",
         "Stability.cpp",
         "Status.cpp",
         "TextOutput.cpp",
-        "IpPrefix.cpp",
         ":libbinder_aidl",
     ],
 
     target: {
+        android: {
+            srcs: libbinder_device_interface_sources,
+        },
         vendor: {
-            exclude_srcs: [
-                "ActivityManager.cpp",
-                "IActivityManager.cpp",
-                "IAppOpsCallback.cpp",
-                "IBatteryStats.cpp",
-                "IMediaResourceMonitor.cpp",
-                "IPermissionController.cpp",
-                "IProcessInfoService.cpp",
-                "IUidObserver.cpp",
-                "PermissionCache.cpp",
-                "PermissionController.cpp",
-                "ProcessInfoService.cpp",
-                "IpPrefix.cpp",
-            ],
+            exclude_srcs: libbinder_device_interface_sources,
         },
     },
 
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index e2af01c..711fed9 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -32,7 +32,6 @@
 
 namespace {
 
-#ifndef __ANDROID_VNDK__
 #if defined(__BRILLO__)
 // Because Brillo has no application model, security policy is managed
 // statically (at build time) with SELinux controls.
@@ -41,17 +40,13 @@
 #else
 const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
 #endif  // defined(__BRILLO__)
-#endif // __ANDROID_VNDK__
 
 }  // namespace
 
 static String16 _appops("appops");
-#ifndef __ANDROID_VNDK__
 static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-#endif // __ANDROID_VNDK__
 static sp<IBinder> gToken;
 
-#ifndef __ANDROID_VNDK__
 static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
     pthread_mutex_lock(&gTokenMutex);
     if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
@@ -60,17 +55,12 @@
     pthread_mutex_unlock(&gTokenMutex);
     return gToken;
 }
-#endif // __ANDROID_VNDK__
 
 thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
 thread_local int32_t uidOfThisBinderTransaction = -1;
 
 // Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
-#ifndef __ANDROID_VNDK__
 uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
-#else
-uint8_t appOpsToNote[128] = {0};
-#endif // __ANDROID_VNDK__
 
 AppOpsManager::AppOpsManager()
 {
@@ -108,7 +98,6 @@
 }
 #endif  // defined(__BRILLO__)
 
-#ifndef __ANDROID_VNDK__
 int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
 {
     sp<IAppOpsService> service = getService();
@@ -200,8 +189,6 @@
     }
 }
 
-#endif // __ANDROID_VNDK__
-
 bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 74ffde2..c5aa007 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -221,6 +221,9 @@
             auto stability = Stability::get(this);
 
             if (CC_UNLIKELY(!Stability::check(stability, Stability::kLocalStability))) {
+                ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
+                    Stability::stabilityString(stability).c_str(),
+                    Stability::stabilityString(Stability::kLocalStability).c_str());
                 return BAD_TYPE;
             }
         }
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b6360cb..c58ea02 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -34,7 +34,6 @@
     {
     }
 
-#ifndef __ANDROID_VNDK__
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -145,7 +144,6 @@
         remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
     }
 
-#endif
     virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
             const String16& packageName, int32_t opCode, const String16& message) {
         Parcel data, reply;
@@ -195,7 +193,6 @@
 {
     //printf("AppOpsService received: "); data.print();
     switch(code) {
-#ifndef __ANDROID_VNDK__
         case CHECK_OPERATION_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             int32_t code = data.readInt32();
@@ -288,7 +285,6 @@
             reply->writeNoException();
             return NO_ERROR;
         } break;
-#endif // __ANDROID_VNDK__
         case NOTE_ASYNC_OP_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             String16 callingPackageName = data.readString16();
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index caf2318..a7662e9 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -149,7 +149,7 @@
     return static_cast<char*>(base) + offset;
 }
 
-void* IMemory::pointer() const {
+void* IMemory::unsecurePointer() const {
     ssize_t offset;
     sp<IMemoryHeap> heap = getMemory(&offset);
     void* const base = heap!=nullptr ? heap->base() : MAP_FAILED;
@@ -158,6 +158,8 @@
     return static_cast<char*>(base) + offset;
 }
 
+void* IMemory::pointer() const { return unsecurePointer(); }
+
 size_t IMemory::size() const {
     size_t size;
     getMemory(nullptr, &size);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 715a460..f3e8f45 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -20,15 +20,19 @@
 
 #include <android/os/BnServiceCallback.h>
 #include <android/os/IServiceManager.h>
-#include <utils/Log.h>
 #include <binder/IPCThreadState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+
 #ifndef __ANDROID_VNDK__
 #include <binder/IPermissionController.h>
 #endif
-#include <binder/Parcel.h>
+
+#ifdef __ANDROID__
 #include <cutils/properties.h>
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
+#endif
 
 #include "Static.h"
 
@@ -59,7 +63,7 @@
     return gDefaultServiceManager;
 }
 
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
 // IPermissionController is not accessible to vendors
 
 bool checkCallingPermission(const String16& permission)
@@ -162,11 +166,15 @@
         const bool isVendorService =
             strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
         const long timeout = uptimeMillis() + 5000;
+        // Vendor code can't access system properties
         if (!gSystemBootCompleted && !isVendorService) {
-            // Vendor code can't access system properties
+#ifdef __ANDROID__
             char bootCompleted[PROPERTY_VALUE_MAX];
             property_get("sys.boot_completed", bootCompleted, "0");
             gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false;
+#else
+            gSystemBootCompleted = true;
+#endif
         }
         // retry interval in millisecond; note that vendor services stay at 100ms
         const long sleepTime = gSystemBootCompleted ? 1000 : 100;
@@ -232,7 +240,7 @@
         const std::string name = String8(name16).c_str();
 
         sp<IBinder> out;
-        if(!mTheRealServiceManager->checkService(name, &out).isOk()) {
+        if (!mTheRealServiceManager->getService(name, &out).isOk()) {
             return nullptr;
         }
         if(out != nullptr) return out;
@@ -256,13 +264,13 @@
             // Handle race condition for lazy services. Here is what can happen:
             // - the service dies (not processed by init yet).
             // - sm processes death notification.
-            // - sm gets checkService and calls init to start service.
+            // - sm gets getService and calls init to start service.
             // - init gets the start signal, but the service already appears
             //   started, so it does nothing.
             // - init gets death signal, but doesn't know it needs to restart
             //   the service
             // - we need to request service again to get it to start
-            if(!mTheRealServiceManager->checkService(name, &out).isOk()) {
+            if (!mTheRealServiceManager->getService(name, &out).isOk()) {
                 return nullptr;
             }
             if(out != nullptr) return out;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 3a7a7a9..2a75619 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -67,7 +67,7 @@
 #define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
 
 static size_t pad_size(size_t s) {
-    if (s > (SIZE_T_MAX - 3)) {
+    if (s > (std::numeric_limits<size_t>::max() - 3)) {
         abort();
     }
     return PAD_SIZE_UNSAFE(s);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 07db50f..eb828c3 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -189,7 +189,8 @@
 }
 
 void ProcessState::setCallRestriction(CallRestriction restriction) {
-    LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started.");
+    LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull() != nullptr,
+        "Call restrictions must be set before the threadpool is started.");
 
     mCallRestriction = restriction;
 }
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index b6f10c8..7ce5e36 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -37,6 +37,10 @@
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
+bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) {
+    return check(get(binder.get()), Level::VINTF);
+}
+
 void Stability::tryMarkCompilationUnit(IBinder* binder) {
     (void) set(binder, kLocalStability, false /*log*/);
 }
@@ -99,12 +103,6 @@
         stable = false;
     }
 
-    if (!stable) {
-        ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
-            stabilityString(provided).c_str(),
-            stabilityString(required).c_str());
-    }
-
     return stable;
 }
 
@@ -123,4 +121,4 @@
 }
 
 }  // namespace internal
-}  // namespace stability
\ No newline at end of file
+}  // namespace stability
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index a6fd8c4..bd40536 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -54,7 +54,9 @@
 protected:
     virtual status_t writeLines(const struct iovec& vec, size_t N)
     {
-        writev(mFD, &vec, N);
+        ssize_t ret = writev(mFD, &vec, N);
+        if (ret == -1) return -errno;
+        if (static_cast<size_t>(ret) != N) return UNKNOWN_ERROR;
         return NO_ERROR;
     }
 
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index dff4d49..f5a54ce 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -21,6 +21,10 @@
 
 #include <utils/threads.h>
 
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -33,7 +37,6 @@
         MODE_ERRORED = IAppOpsService::MODE_ERRORED
     };
 
-#ifndef __ANDROID_VNDK__
     enum {
         OP_NONE = -1,
         OP_COARSE_LOCATION = 0,
@@ -121,11 +124,9 @@
         OP_READ_DEVICE_IDENTIFIERS = 89,
         _NUM_OP = 90
     };
-#endif // __ANDROID_VNDK__
 
     AppOpsManager();
 
-#ifndef __ANDROID_VNDK__
     int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
     int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
             const String16& callingPackage);
@@ -145,7 +146,6 @@
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
     void setCameraAudioRestriction(int32_t mode);
-#endif // __ANDROID_VNDK__
     void noteAsyncOp(const String16& callingPackageName, int32_t uid, const String16& packageName,
             int32_t opCode, const String16& message);
 
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 009ef6c..9784003 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -18,11 +18,13 @@
 #ifndef ANDROID_IAPP_OPS_SERVICE_H
 #define ANDROID_IAPP_OPS_SERVICE_H
 
-#ifndef __ANDROID_VNDK__
 #include <binder/IAppOpsCallback.h>
-#endif
 #include <binder/IInterface.h>
 
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
 namespace android {
 
 // ----------------------------------------------------------------------
@@ -32,7 +34,6 @@
 public:
     DECLARE_META_INTERFACE(AppOpsService)
 
-#ifndef __ANDROID_VNDK__
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
@@ -47,13 +48,11 @@
     virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
             const String16& packageName) = 0;
     virtual void setCameraAudioRestriction(int32_t mode) = 0;
-#endif // __ANDROID_VNDK__
     virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid,
             const String16& packageName, int32_t opCode, const String16& message) = 0;
     virtual bool shouldCollectNotes(int32_t opCode) = 0;
 
     enum {
-#ifndef __ANDROID_VNDK__
         CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
         START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
@@ -63,13 +62,9 @@
         GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
         PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
         CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
-#endif // __ANDROID_VNDK__
         NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
         SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
-#ifndef __ANDROID_VNDK__
         SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11,
-#endif // __ANDROID_VNDK__
-
     };
 
     enum {
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 408037e..027e088 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -22,9 +22,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
-
-// linux/binder.h already defines this, but we can't just include it from there
-// because there are host builds that include this file.
+// linux/binder.h defines this, but we don't want to include it here in order to
+// avoid exporting the kernel headers
 #ifndef B_PACK_CHARS
 #define B_PACK_CHARS(c1, c2, c3, c4) \
     ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 071946f..8791741 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -77,10 +77,33 @@
     virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0;
 
     // helpers
-    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
-    void* pointer() const;
+
+    // Accessing the underlying pointer must be done with caution, as there are
+    // some inherent security risks associated with it. When receiving an
+    // IMemory from an untrusted process, there is currently no way to guarantee
+    // that this process would't change the content after the fact. This may
+    // lead to TOC/TOU class of security bugs. In most cases, when performance
+    // is not an issue, the recommended practice is to immediately copy the
+    // buffer upon reception, then work with the copy, e.g.:
+    //
+    // std::string private_copy(mem.size(), '\0');
+    // memcpy(private_copy.data(), mem.unsecurePointer(), mem.size());
+    //
+    // In cases where performance is an issue, this matter must be addressed on
+    // an ad-hoc basis.
+    void* unsecurePointer() const;
+
     size_t size() const;
     ssize_t offset() const;
+
+private:
+    // These are now deprecated and are left here for backward-compatibility
+    // with prebuilts that may reference these symbol at runtime.
+    // Instead, new code should use unsecurePointer()/unsecureFastPointer(),
+    // which do the same thing, but make it more obvious that there are some
+    // security-related pitfalls associated with them.
+    void* pointer() const;
+    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
 };
 
 class BnMemory : public BnInterface<IMemory>
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b1b8ff1..3471e13 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -33,9 +33,9 @@
 #include <binder/Parcelable.h>
 
 #ifdef BINDER_IPC_32BIT
-typedef __u32 binder_size_t;
+typedef unsigned int binder_size_t;
 #else
-typedef __u64 binder_size_t;
+typedef unsigned long long binder_size_t;
 #endif
 
 
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index f8240e4..b84657a 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -57,6 +57,9 @@
     // break the device during GSI or other tests.
     static void markVndk(IBinder* binder);
 
+    // Returns true if the binder needs to be declared in the VINTF manifest or
+    // else false if the binder is local to the current partition.
+    static bool requiresVintfDeclaration(const sp<IBinder>& binder);
 private:
     // Parcel needs to read/write stability level in an unstable format.
     friend ::android::Parcel;
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 2f11622..c22be9f 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -23,6 +23,16 @@
 
 /* obtain structures and constants from the kernel header */
 
+// TODO(b/31559095): bionic on host
+#ifndef __ANDROID__
+#define __packed __attribute__((__packed__))
+#endif
+
+// TODO(b/31559095): bionic on host
+#if defined(B_PACK_CHARS) && !defined(_UAPI_LINUX_BINDER_H)
+#undef B_PACK_CHARS
+#endif
+
 #include <sys/ioctl.h>
 #include <linux/android/binder.h>
 
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 734a928..62a0f9f 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -14,9 +14,25 @@
  * limitations under the License.
  */
 
+// TODO(b/31559095): bionic on host should define this
+cc_defaults {
+    name: "libbinder_ndk_host_user",
+    target: {
+        host: {
+            cflags: [
+                "-D__INTRODUCED_IN(n)=",
+                "-D__assert(a,b,c)=",
+            ],
+        },
+    },
+}
+
 cc_library_shared {
     name: "libbinder_ndk",
 
+    defaults: ["libbinder_ndk_host_user"],
+    host_supported: true,
+
     export_include_dirs: [
         "include_ndk",
         "include_platform",
@@ -52,10 +68,17 @@
         "jni_headers",
     ],
 
-    version_script: "libbinder_ndk.map.txt",
+    target: {
+        linux: {
+            version_script: "libbinder_ndk.map.txt",
+        },
+    },
     stubs: {
         symbol_file: "libbinder_ndk.map.txt",
-        versions: ["29", "30"],
+        versions: [
+            "29",
+            "30",
+        ],
     },
 }
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 2258210..8c41707 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include <stddef.h>
 #include <sys/cdefs.h>
 
 #include <android/binder_status.h>
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index bc457ce..635ea69 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -70,6 +70,7 @@
         "libutils",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
 
 cc_test {
@@ -136,6 +137,7 @@
         "libutils",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
 
 aidl_interface {
@@ -164,4 +166,5 @@
     ],
 
     test_suites: ["device-tests"],
+    require_root: true,
 }
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 0336b9e..0bee56c 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -122,6 +122,32 @@
     }
 };
 
+TEST(BinderStability, OnlyVintfStabilityBinderNeedsVintfDeclaration) {
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(nullptr));
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::undef()));
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::system()));
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::vendor()));
+
+    EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
+}
+
+TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
+    sp<IBinder> vintfServer = BadStableBinder::vintf();
+
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("."), vintfServer));
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("/"), vintfServer));
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("/."), vintfServer));
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("a.d.IFoo"), vintfServer));
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("foo"), vintfServer));
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+        android::defaultServiceManager()->addService(String16("a.d.IFoo/foo"), vintfServer));
+}
+
 TEST(BinderStability, CantCallVendorBinderInSystemContext) {
     sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
     auto server = interface_cast<IBinderStabilityTest>(serverBinder);
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 512b069..ee1a6a4 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -20,6 +20,8 @@
         enabled: true,
         support_system_process: true,
     },
+    host_supported: true,
+
     srcs: [
         "IPCThreadStateBase.cpp",
     ],
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 4c8d52e..f255512 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -25,6 +25,7 @@
 #include <sys/sysinfo.h>
 
 #include <mutex>
+#include <numeric>
 #include <optional>
 #include <set>
 #include <string>
@@ -53,7 +54,8 @@
 static std::vector<std::vector<uint32_t>> gPolicyFreqs;
 static std::vector<std::vector<uint32_t>> gPolicyCpus;
 static std::set<uint32_t> gAllFreqs;
-static unique_fd gMapFd;
+static unique_fd gTisMapFd;
+static unique_fd gConcurrentMapFd;
 
 static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) {
     std::string data;
@@ -122,8 +124,12 @@
         gPolicyCpus.emplace_back(*cpus);
     }
 
-    gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times_map")};
-    if (gMapFd < 0) return false;
+    gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
+    if (gTisMapFd < 0) return false;
+
+    gConcurrentMapFd =
+            unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+    if (gConcurrentMapFd < 0) return false;
 
     gInitialized = true;
     return true;
@@ -143,7 +149,7 @@
 // process dies then it must be called again to resume tracking.
 // This function should *not* be called while tracking is already active; doing so is unnecessary
 // and can lead to accounting errors.
-bool startTrackingUidCpuFreqTimes() {
+bool startTrackingUidTimes() {
     if (!initGlobals()) return false;
 
     unique_fd fd(bpf_obj_get(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
@@ -174,7 +180,7 @@
             attachTracepointProgram("power", "cpu_frequency");
 }
 
-// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
+// Retrieve the times in ns that uid spent running at each CPU frequency.
 // Return contains no value on error, otherwise it contains a vector of vectors using the format:
 // [[t0_0, t0_1, ...],
 //  [t1_0, t1_1, ...], ...]
@@ -189,11 +195,11 @@
         out.emplace_back(freqList.size(), 0);
     }
 
-    std::vector<val_t> vals(gNCpus);
+    std::vector<tis_val_t> vals(gNCpus);
     time_key_t key = {.uid = uid};
     for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
         key.bucket = i;
-        if (findMapEntry(gMapFd, &key, vals.data())) {
+        if (findMapEntry(gTisMapFd, &key, vals.data())) {
             if (errno != ENOENT) return {};
             continue;
         }
@@ -214,7 +220,7 @@
     return out;
 }
 
-// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
+// Retrieve the times in ns that each uid spent running at each CPU freq.
 // Return contains no value on error, otherwise it contains a map from uids to vectors of vectors
 // using the format:
 // { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
@@ -225,7 +231,7 @@
     if (!gInitialized && !initGlobals()) return {};
     time_key_t key, prevKey;
     std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map;
-    if (getFirstMapKey(gMapFd, &key)) {
+    if (getFirstMapKey(gTisMapFd, &key)) {
         if (errno == ENOENT) return map;
         return std::nullopt;
     }
@@ -233,9 +239,9 @@
     std::vector<std::vector<uint64_t>> mapFormat;
     for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0);
 
-    std::vector<val_t> vals(gNCpus);
+    std::vector<tis_val_t> vals(gNCpus);
     do {
-        if (findMapEntry(gMapFd, &key, vals.data())) return {};
+        if (findMapEntry(gTisMapFd, &key, vals.data())) return {};
         if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat);
 
         auto offset = key.bucket * FREQS_PER_ENTRY;
@@ -250,13 +256,129 @@
             }
         }
         prevKey = key;
-    } while (!getNextMapKey(gMapFd, &prevKey, &key));
+    } while (!getNextMapKey(gTisMapFd, &prevKey, &key));
     if (errno != ENOENT) return {};
     return map;
 }
 
+static bool verifyConcurrentTimes(const concurrent_time_t &ct) {
+    uint64_t activeSum = std::accumulate(ct.active.begin(), ct.active.end(), (uint64_t)0);
+    uint64_t policySum = 0;
+    for (const auto &vec : ct.policy) {
+        policySum += std::accumulate(vec.begin(), vec.end(), (uint64_t)0);
+    }
+    return activeSum == policySum;
+}
+
+// Retrieve the times in ns that uid spent running concurrently with each possible number of other
+// tasks on each cluster (policy times) and overall (active times).
+// Return contains no value on error, otherwise it contains a concurrent_time_t with the format:
+// {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...]}
+// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
+// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster
+std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) {
+    if (!gInitialized && !initGlobals()) return {};
+    concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)};
+    for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0);
+    std::vector<concurrent_val_t> vals(gNCpus);
+    time_key_t key = {.uid = uid};
+    for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+        if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
+            if (errno != ENOENT) return {};
+            continue;
+        }
+        auto offset = key.bucket * CPUS_PER_ENTRY;
+        auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY;
+
+        auto activeBegin = ret.active.begin() + offset;
+        auto activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret.active.end();
+
+        for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) {
+            std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin,
+                           std::plus<uint64_t>());
+        }
+
+        for (uint32_t policy = 0; policy < gNPolicies; ++policy) {
+            if (offset >= gPolicyCpus[policy].size()) continue;
+            auto policyBegin = ret.policy[policy].begin() + offset;
+            auto policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY
+                                                                     : ret.policy[policy].end();
+
+            for (const auto &cpu : gPolicyCpus[policy]) {
+                std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
+                               std::plus<uint64_t>());
+            }
+        }
+    }
+    if (!verifyConcurrentTimes(ret) && retry)  return getUidConcurrentTimes(uid, false);
+    return ret;
+}
+
+// Retrieve the times in ns that each uid spent running concurrently with each possible number of
+// other tasks on each cluster (policy times) and overall (active times).
+// Return contains no value on error, otherwise it contains a map from uids to concurrent_time_t's
+// using the format:
+// { uid0 -> {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...] }, ...}
+// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
+// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster.
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() {
+    if (!gInitialized && !initGlobals()) return {};
+    time_key_t key, prevKey;
+    std::unordered_map<uint32_t, concurrent_time_t> ret;
+    if (getFirstMapKey(gConcurrentMapFd, &key)) {
+        if (errno == ENOENT) return ret;
+        return {};
+    }
+
+    concurrent_time_t retFormat = {.active = std::vector<uint64_t>(gNCpus, 0)};
+    for (const auto &cpuList : gPolicyCpus) retFormat.policy.emplace_back(cpuList.size(), 0);
+
+    std::vector<concurrent_val_t> vals(gNCpus);
+    std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd;
+
+    do {
+        if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {};
+        if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat);
+
+        auto offset = key.bucket * CPUS_PER_ENTRY;
+        auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY;
+
+        activeBegin = ret[key.uid].active.begin();
+        activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret[key.uid].active.end();
+
+        for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) {
+            std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin,
+                           std::plus<uint64_t>());
+        }
+
+        for (uint32_t policy = 0; policy < gNPolicies; ++policy) {
+            if (offset >= gPolicyCpus[policy].size()) continue;
+            policyBegin = ret[key.uid].policy[policy].begin() + offset;
+            policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY
+                                                                : ret[key.uid].policy[policy].end();
+
+            for (const auto &cpu : gPolicyCpus[policy]) {
+                std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
+                               std::plus<uint64_t>());
+            }
+        }
+        prevKey = key;
+    } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key));
+    if (errno != ENOENT) return {};
+    for (const auto &[key, value] : ret) {
+        if (!verifyConcurrentTimes(value)) {
+            auto val = getUidConcurrentTimes(key, false);
+            if (val.has_value()) ret[key] = val.value();
+        }
+    }
+    return ret;
+}
+
 // Clear all time in state data for a given uid. Returns false on error, true otherwise.
-bool clearUidCpuFreqTimes(uint32_t uid) {
+// This is only suitable for clearing data when an app is uninstalled; if called on a UID with
+// running tasks it will cause time in state vs. concurrent time totals to be inconsistent for that
+// UID.
+bool clearUidTimes(uint32_t uid) {
     if (!gInitialized && !initGlobals()) return false;
 
     time_key_t key = {.uid = uid};
@@ -266,11 +388,20 @@
         if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
     }
 
-    val_t zeros = {0};
-    std::vector<val_t> vals(gNCpus, zeros);
+    tis_val_t zeros = {0};
+    std::vector<tis_val_t> vals(gNCpus, zeros);
     for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) {
-        if (writeToMapEntry(gMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT) return false;
-        if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+        if (writeToMapEntry(gTisMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT)
+            return false;
+        if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false;
+    }
+
+    concurrent_val_t czeros = {.policy = {0}, .active = {0}};
+    std::vector<concurrent_val_t> cvals(gNCpus, czeros);
+    for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+        if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT)
+            return false;
+        if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false;
     }
     return true;
 }
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index d7b4587..f620715 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -22,11 +22,19 @@
 namespace android {
 namespace bpf {
 
-bool startTrackingUidCpuFreqTimes();
+bool startTrackingUidTimes();
 std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
     getUidsCpuFreqTimes();
-bool clearUidCpuFreqTimes(unsigned int uid);
+
+struct concurrent_time_t {
+    std::vector<uint64_t> active;
+    std::vector<std::vector<uint64_t>> policy;
+};
+
+std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true);
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes();
+bool clearUidTimes(unsigned int uid);
 
 } // namespace bpf
 } // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 39007e4..15f6214 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -3,6 +3,7 @@
 
 #include <sys/sysinfo.h>
 
+#include <numeric>
 #include <unordered_map>
 #include <vector>
 
@@ -21,13 +22,83 @@
 
 using std::vector;
 
-TEST(TimeInStateTest, SingleUid) {
+TEST(TimeInStateTest, SingleUidTimeInState) {
     auto times = getUidCpuFreqTimes(0);
     ASSERT_TRUE(times.has_value());
     EXPECT_FALSE(times->empty());
 }
 
-TEST(TimeInStateTest, AllUid) {
+TEST(TimeInStateTest, SingleUidConcurrentTimes) {
+    auto concurrentTimes = getUidConcurrentTimes(0);
+    ASSERT_TRUE(concurrentTimes.has_value());
+    ASSERT_FALSE(concurrentTimes->active.empty());
+    ASSERT_FALSE(concurrentTimes->policy.empty());
+
+    uint64_t policyEntries = 0;
+    for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
+    ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
+}
+
+static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
+    size_t maxPolicyCpus = 0;
+    for (const auto &vec : concurrentTime.policy) {
+        maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
+    }
+    uint64_t policySum = 0;
+    for (size_t i = 0; i < maxPolicyCpus; ++i) {
+        for (const auto &vec : concurrentTime.policy) {
+            if (i < vec.size()) policySum += vec[i];
+        }
+        ASSERT_LE(concurrentTime.active[i], policySum);
+        policySum -= concurrentTime.active[i];
+    }
+    policySum = 0;
+    for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
+        for (const auto &vec : concurrentTime.policy) {
+            if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
+        }
+        auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
+        // This check is slightly flaky because we may read a map entry in the middle of an update
+        // when active times have been updated but policy times have not. This happens infrequently
+        // and can be distinguished from more serious bugs by re-running the test: if the underlying
+        // data itself is inconsistent, the test will fail every time.
+        ASSERT_LE(activeSum, policySum);
+        policySum -= activeSum;
+    }
+}
+
+static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
+                                   const struct concurrent_time_t &concurrentTime) {
+    ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
+    ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
+    uint64_t policySum = 0;
+    for (uint32_t i = 0; i < timeInState.size(); ++i) {
+        uint64_t tisSum =
+                std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
+        uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
+                                                 concurrentTime.policy[i].end(), (uint64_t)0);
+        if (tisSum < concurrentSum)
+            ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
+        else
+            ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
+        policySum += concurrentSum;
+    }
+    uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
+                                         (uint64_t)0);
+    EXPECT_EQ(activeSum, policySum);
+}
+
+TEST(TimeInStateTest, SingleUidTimesConsistent) {
+    auto times = getUidCpuFreqTimes(0);
+    ASSERT_TRUE(times.has_value());
+
+    auto concurrentTimes = getUidConcurrentTimes(0);
+    ASSERT_TRUE(concurrentTimes.has_value());
+
+    ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
+}
+
+TEST(TimeInStateTest, AllUidTimeInState) {
     vector<size_t> sizes;
     auto map = getUidsCpuFreqTimes();
     ASSERT_TRUE(map.has_value());
@@ -43,7 +114,7 @@
     }
 }
 
-TEST(TimeInStateTest, SingleAndAllUidConsistent) {
+TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
     auto map = getUidsCpuFreqTimes();
     ASSERT_TRUE(map.has_value());
     ASSERT_FALSE(map->empty());
@@ -64,6 +135,40 @@
     }
 }
 
+TEST(TimeInStateTest, AllUidConcurrentTimes) {
+    auto map = getUidsConcurrentTimes();
+    ASSERT_TRUE(map.has_value());
+    ASSERT_FALSE(map->empty());
+
+    auto firstEntry = map->begin()->second;
+    for (const auto &kv : *map) {
+        ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
+        ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
+        for (size_t i = 0; i < kv.second.policy.size(); ++i) {
+            ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+        }
+    }
+}
+
+TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
+    auto map = getUidsConcurrentTimes();
+    ASSERT_TRUE(map.has_value());
+    for (const auto &kv : *map) {
+        uint32_t uid = kv.first;
+        auto times1 = kv.second;
+        auto times2 = getUidConcurrentTimes(uid);
+        ASSERT_TRUE(times2.has_value());
+        for (uint32_t i = 0; i < times1.active.size(); ++i) {
+            ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+        }
+        for (uint32_t i = 0; i < times1.policy.size(); ++i) {
+            for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
+                ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+            }
+        }
+    }
+}
+
 void TestCheckDelta(uint64_t before, uint64_t after) {
     // Times should never decrease
     ASSERT_LE(before, after);
@@ -71,7 +176,7 @@
     ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
 }
 
-TEST(TimeInStateTest, AllUidMonotonic) {
+TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
     auto map1 = getUidsCpuFreqTimes();
     ASSERT_TRUE(map1.has_value());
     sleep(1);
@@ -92,7 +197,35 @@
     }
 }
 
-TEST(TimeInStateTest, AllUidSanityCheck) {
+TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
+    auto map1 = getUidsConcurrentTimes();
+    ASSERT_TRUE(map1.has_value());
+    ASSERT_FALSE(map1->empty());
+    sleep(1);
+    auto map2 = getUidsConcurrentTimes();
+    ASSERT_TRUE(map2.has_value());
+    ASSERT_FALSE(map2->empty());
+
+    for (const auto &kv : *map1) {
+        uint32_t uid = kv.first;
+        auto times = kv.second;
+        ASSERT_NE(map2->find(uid), map2->end());
+        for (uint32_t i = 0; i < times.active.size(); ++i) {
+            auto before = times.active[i];
+            auto after = (*map2)[uid].active[i];
+            ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
+        }
+        for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
+            for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
+                auto before = times.policy[policy][idx];
+                auto after = (*map2)[uid].policy[policy][idx];
+                ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
+            }
+        }
+    }
+}
+
+TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
     auto map = getUidsCpuFreqTimes();
     ASSERT_TRUE(map.has_value());
 
@@ -110,6 +243,48 @@
     ASSERT_TRUE(foundLargeValue);
 }
 
+TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
+    auto concurrentMap = getUidsConcurrentTimes();
+    ASSERT_TRUE(concurrentMap);
+
+    bool activeFoundLargeValue = false;
+    bool policyFoundLargeValue = false;
+    for (const auto &kv : *concurrentMap) {
+        for (const auto &time : kv.second.active) {
+            ASSERT_LE(time, NSEC_PER_YEAR);
+            if (time > UINT32_MAX) activeFoundLargeValue = true;
+        }
+        for (const auto &policyTimeVec : kv.second.policy) {
+            for (const auto &time : policyTimeVec) {
+                ASSERT_LE(time, NSEC_PER_YEAR);
+                if (time > UINT32_MAX) policyFoundLargeValue = true;
+            }
+        }
+    }
+    // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+    // uint64_t as expected, we should have some times higher than that.
+    ASSERT_TRUE(activeFoundLargeValue);
+    ASSERT_TRUE(policyFoundLargeValue);
+}
+
+TEST(TimeInStateTest, AllUidTimesConsistent) {
+    auto tisMap = getUidsCpuFreqTimes();
+    ASSERT_TRUE(tisMap.has_value());
+
+    auto concurrentMap = getUidsConcurrentTimes();
+    ASSERT_TRUE(concurrentMap.has_value());
+
+    ASSERT_EQ(tisMap->size(), concurrentMap->size());
+    for (const auto &kv : *tisMap) {
+        uint32_t uid = kv.first;
+        auto times = kv.second;
+        ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
+
+        auto concurrentTimes = (*concurrentMap)[uid];
+        ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
+    }
+}
+
 TEST(TimeInStateTest, RemoveUid) {
     uint32_t uid = 0;
     {
@@ -122,31 +297,58 @@
     }
     {
         // Add a map entry for our fake UID by copying a real map entry
-        android::base::unique_fd fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times_map")};
+        android::base::unique_fd fd{
+                bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
         ASSERT_GE(fd, 0);
         time_key_t k;
         ASSERT_FALSE(getFirstMapKey(fd, &k));
-        std::vector<val_t> vals(get_nprocs_conf());
+        std::vector<tis_val_t> vals(get_nprocs_conf());
         ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
+        uint32_t copiedUid = k.uid;
         k.uid = uid;
         ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
+
+        android::base::unique_fd fd2{
+                bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+        k.uid = copiedUid;
+        k.bucket = 0;
+        std::vector<concurrent_val_t> cvals(get_nprocs_conf());
+        ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
+        k.uid = uid;
+        ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
     }
     auto times = getUidCpuFreqTimes(uid);
     ASSERT_TRUE(times.has_value());
     ASSERT_FALSE(times->empty());
 
+    auto concurrentTimes = getUidConcurrentTimes(0);
+    ASSERT_TRUE(concurrentTimes.has_value());
+    ASSERT_FALSE(concurrentTimes->active.empty());
+    ASSERT_FALSE(concurrentTimes->policy.empty());
+
     uint64_t sum = 0;
     for (size_t i = 0; i < times->size(); ++i) {
         for (auto x : (*times)[i]) sum += x;
     }
     ASSERT_GT(sum, (uint64_t)0);
 
-    ASSERT_TRUE(clearUidCpuFreqTimes(uid));
+    uint64_t activeSum = 0;
+    for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
+        activeSum += concurrentTimes->active[i];
+    }
+    ASSERT_GT(activeSum, (uint64_t)0);
+
+    ASSERT_TRUE(clearUidTimes(uid));
 
     auto allTimes = getUidsCpuFreqTimes();
     ASSERT_TRUE(allTimes.has_value());
     ASSERT_FALSE(allTimes->empty());
     ASSERT_EQ(allTimes->find(uid), allTimes->end());
+
+    auto allConcurrentTimes = getUidsConcurrentTimes();
+    ASSERT_TRUE(allConcurrentTimes.has_value());
+    ASSERT_FALSE(allConcurrentTimes->empty());
+    ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
 }
 
 } // namespace bpf
diff --git a/libs/cputimeinstate/timeinstate.h b/libs/cputimeinstate/timeinstate.h
index 41d0af0..6d4f913 100644
--- a/libs/cputimeinstate/timeinstate.h
+++ b/libs/cputimeinstate/timeinstate.h
@@ -19,16 +19,22 @@
 #define BPF_FS_PATH "/sys/fs/bpf/"
 
 #define FREQS_PER_ENTRY 32
+#define CPUS_PER_ENTRY 8
 
 struct time_key_t {
     uint32_t uid;
     uint32_t bucket;
 };
 
-struct val_t {
+struct tis_val_t {
     uint64_t ar[FREQS_PER_ENTRY];
 };
 
+struct concurrent_val_t {
+    uint64_t active[CPUS_PER_ENTRY];
+    uint64_t policy[CPUS_PER_ENTRY];
+};
+
 struct freq_idx_key_t {
     uint32_t policy;
     uint32_t freq;
diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp
index e23de8e..e403d36 100644
--- a/libs/dumputils/Android.bp
+++ b/libs/dumputils/Android.bp
@@ -18,7 +18,6 @@
     shared_libs: [
         "libbase",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
     ],
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8e762f1..250f902 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -46,6 +46,7 @@
 static const char* hal_interfaces_to_dump[] {
         "android.hardware.audio@2.0::IDevicesFactory",
         "android.hardware.audio@4.0::IDevicesFactory",
+        "android.hardware.audio@5.0::IDevicesFactory",
         "android.hardware.biometrics.face@1.0::IBiometricsFace",
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index f11cf62..642c5f2 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -33,7 +33,7 @@
     ],
 
     header_libs: [
-        "libnativeloader-dummy-headers",
+        "libnativeloader-headers",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
new file mode 100644
index 0000000..c0bb75f
--- /dev/null
+++ b/libs/graphicsenv/OWNERS
@@ -0,0 +1,6 @@
+chrisforbes@google.com
+cnorthrop@google.com
+courtneygo@google.com
+lpy@google.com
+timvp@google.com
+zzyiwei@google.com
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index beb13ad..3f8b436 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -170,8 +170,6 @@
         "libEGL",
         "libGLESv2",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libnativewindow",
         "libsync",
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index add3ef0..058cd9a 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -28,8 +28,8 @@
         size += sizeof(cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        size += sizeof(size_t);
-        size += hdr10plus.size();
+        size += sizeof(uint32_t);
+        size += hdr10plus.size() * sizeof(hdr10plus[0]);
     }
     return size;
 }
@@ -47,10 +47,11 @@
         FlattenableUtils::write(buffer, size, cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        size_t metadataSize = hdr10plus.size();
+        uint32_t metadataSize = hdr10plus.size();
         FlattenableUtils::write(buffer, size, metadataSize);
-        memcpy(buffer, hdr10plus.data(), metadataSize);
-        FlattenableUtils::advance(buffer, size, metadataSize);
+        size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+        memcpy(buffer, hdr10plus.data(), metadataSizeinByte);
+        FlattenableUtils::advance(buffer, size, metadataSizeinByte);
     }
 
     return NO_ERROR;
@@ -74,20 +75,21 @@
         FlattenableUtils::read(buffer, size, cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        if (size < sizeof(size_t)) {
+        if (size < sizeof(uint32_t)) {
             return NO_MEMORY;
         }
 
-        size_t metadataSize;
+        uint32_t metadataSize;
         FlattenableUtils::read(buffer, size, metadataSize);
 
-        if (size < metadataSize) {
+        size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+        if (size < metadataSizeinByte) {
             return NO_MEMORY;
         }
 
         hdr10plus.resize(metadataSize);
-        memcpy(hdr10plus.data(), buffer, metadataSize);
-        FlattenableUtils::advance(buffer, size, metadataSize);
+        memcpy(hdr10plus.data(), buffer, metadataSizeinByte);
+        FlattenableUtils::advance(buffer, size, metadataSizeinByte);
     }
 
     return NO_ERROR;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index dc161b7..5805797 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -69,7 +69,7 @@
                                      const sp<IBinder>& applyToken,
                                      const InputWindowCommands& commands,
                                      int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer,
+                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                      const std::vector<ListenerCallbacks>& listenerCallbacks) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -90,6 +90,7 @@
         data.writeInt64(desiredPresentTime);
         data.writeStrongBinder(uncacheBuffer.token.promote());
         data.writeUint64(uncacheBuffer.id);
+        data.writeBool(hasListenerCallbacks);
 
         if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
             for (const auto& [listener, callbackIds] : listenerCallbacks) {
@@ -1039,6 +1040,8 @@
             uncachedBuffer.token = data.readStrongBinder();
             uncachedBuffer.id = data.readUint64();
 
+            bool hasListenerCallbacks = data.readBool();
+
             std::vector<ListenerCallbacks> listenerCallbacks;
             int32_t listenersSize = data.readInt32();
             for (int32_t i = 0; i < listenersSize; i++) {
@@ -1047,9 +1050,9 @@
                 data.readInt64Vector(&callbackIds);
                 listenerCallbacks.emplace_back(listener, callbackIds);
             }
-
             setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
-                                desiredPresentTime, uncachedBuffer, listenerCallbacks);
+                                desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
+                                listenerCallbacks);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 523ed1d..e004e95 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,7 +86,6 @@
     memcpy(output.writeInplace(16 * sizeof(float)),
            colorTransform.asArray(), 16 * sizeof(float));
     output.writeFloat(cornerRadius);
-    output.writeBool(hasListenerCallbacks);
     output.writeStrongBinder(cachedBuffer.token.promote());
     output.writeUint64(cachedBuffer.id);
     output.writeParcelable(metadata);
@@ -95,6 +94,22 @@
     output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
     output.writeBool(colorSpaceAgnostic);
 
+    auto err = output.writeVectorSize(listeners);
+    if (err) {
+        return err;
+    }
+
+    for (auto listener : listeners) {
+        err = output.writeStrongBinder(listener.transactionCompletedListener);
+        if (err) {
+            return err;
+        }
+        err = output.writeInt64Vector(listener.callbackIds);
+        if (err) {
+            return err;
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -156,7 +171,6 @@
 
     colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
     cornerRadius = input.readFloat();
-    hasListenerCallbacks = input.readBool();
     cachedBuffer.token = input.readStrongBinder();
     cachedBuffer.id = input.readUint64();
     input.readParcelable(&metadata);
@@ -165,6 +179,14 @@
     bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
     colorSpaceAgnostic = input.readBool();
 
+    int32_t numListeners = input.readInt32();
+    listeners.clear();
+    for (int i = 0; i < numListeners; i++) {
+        auto listener = input.readStrongBinder();
+        std::vector<CallbackId> callbackIds;
+        input.readInt64Vector(&callbackIds);
+        listeners.emplace_back(listener, callbackIds);
+    }
     return NO_ERROR;
 }
 
@@ -361,7 +383,6 @@
     }
     if (other.what & eHasListenerCallbacksChanged) {
         what |= eHasListenerCallbacksChanged;
-        hasListenerCallbacks = other.hasListenerCallbacks;
     }
 
 #ifndef NO_INPUT
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 7e356e4..fb9d742 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1081,6 +1081,12 @@
     case NATIVE_WINDOW_SET_AUTO_PREROTATION:
         res = dispatchSetAutoPrerotation(args);
         break;
+    case NATIVE_WINDOW_GET_LAST_DEQUEUE_START:
+        res = dispatchGetLastDequeueStartTime(args);
+        break;
+    case NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT:
+        res = dispatchSetDequeueTimeout(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1286,6 +1292,17 @@
     return setAutoPrerotation(autoPrerotation);
 }
 
+int Surface::dispatchGetLastDequeueStartTime(va_list args) {
+    int64_t* lastDequeueStartTime = va_arg(args, int64_t*);
+    *lastDequeueStartTime = mLastDequeueStartTime;
+    return NO_ERROR;
+}
+
+int Surface::dispatchSetDequeueTimeout(va_list args) {
+    nsecs_t timeout = va_arg(args, int64_t);
+    return setDequeueTimeout(timeout);
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -1950,11 +1967,6 @@
     return mGraphicBufferProducer->getConsumerUsage(outUsage);
 }
 
-nsecs_t Surface::getLastDequeueStartTime() const {
-    Mutex::Autolock lock(mMutex);
-    return mLastDequeueStartTime;
-}
-
 status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
     if (out == nullptr) {
         ALOGE("%s: out must not be null!", __FUNCTION__);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 67dd726..6b5021d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -366,6 +366,33 @@
     if (count > parcel->dataSize()) {
         return BAD_VALUE;
     }
+    std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
+    listenerCallbacks.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        sp<ITransactionCompletedListener> listener =
+                interface_cast<ITransactionCompletedListener>(parcel->readStrongBinder());
+        size_t numCallbackIds = parcel->readUint32();
+        if (numCallbackIds > parcel->dataSize()) {
+            return BAD_VALUE;
+        }
+        for (size_t j = 0; j < numCallbackIds; j++) {
+            listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+        }
+        size_t numSurfaces = parcel->readUint32();
+        if (numSurfaces > parcel->dataSize()) {
+            return BAD_VALUE;
+        }
+        for (size_t j = 0; j < numSurfaces; j++) {
+            sp<SurfaceControl> surface;
+            surface = SurfaceControl::readFromParcel(parcel);
+            listenerCallbacks[listener].surfaceControls.insert(surface);
+        }
+    }
+
+    count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
     std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
     composerStates.reserve(count);
     for (size_t i = 0; i < count; i++) {
@@ -389,10 +416,9 @@
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
     mDisplayStates = displayStates;
+    mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
     mInputWindowCommands = inputWindowCommands;
-    // listener callbacks contain function pointer addresses and may not be safe to parcel.
-    mListenerCallbacks.clear();
     return NO_ERROR;
 }
 
@@ -408,6 +434,19 @@
         displayState.write(*parcel);
     }
 
+    parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
+    for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
+        parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
+        parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
+        for (auto callbackId : callbackInfo.callbackIds) {
+            parcel->writeInt64(callbackId);
+        }
+        parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
+        for (auto surfaceControl : callbackInfo.surfaceControls) {
+            surfaceControl->writeToParcel(parcel);
+        }
+    }
+
     parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
     for (auto const& [surfaceHandle, composerState] : mComposerStates) {
         parcel->writeStrongBinder(surfaceHandle);
@@ -441,6 +480,11 @@
         mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
                                                                 callbackIds.begin()),
                                                         std::make_move_iterator(callbackIds.end()));
+        // register surface controls for this listener that is merging
+        for (const auto& surfaceControl : surfaceControls) {
+            registerSurfaceControlForCallback(surfaceControl);
+        }
+
         mListenerCallbacks[listener]
                 .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
                                         std::make_move_iterator(surfaceControls.end()));
@@ -479,7 +523,7 @@
 
     composerStates.add(s);
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {});
+    sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, false, {});
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -490,7 +534,7 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {});
+    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -539,8 +583,8 @@
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
+    bool hasListenerCallbacks = !mListenerCallbacks.empty();
     std::vector<ListenerCallbacks> listenerCallbacks;
-
     // For every listener with registered callbacks
     for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
         auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -548,19 +592,24 @@
             continue;
         }
 
-        listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
-
-        // If the listener has any SurfaceControls set on this Transaction update the surface state
-        for (const auto& surfaceControl : surfaceControls) {
-            layer_state_t* s = getLayerState(surfaceControl);
-            if (!s) {
-                ALOGE("failed to get layer state");
-                continue;
+        if (surfaceControls.empty()) {
+            listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+        } else {
+            // If the listener has any SurfaceControls set on this Transaction update the surface
+            // state
+            for (const auto& surfaceControl : surfaceControls) {
+                layer_state_t* s = getLayerState(surfaceControl);
+                if (!s) {
+                    ALOGE("failed to get layer state");
+                    continue;
+                }
+                std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
+                s->what |= layer_state_t::eHasListenerCallbacksChanged;
+                s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
             }
-            s->what |= layer_state_t::eHasListenerCallbacksChanged;
-            s->hasListenerCallbacks = true;
         }
     }
+
     mListenerCallbacks.clear();
 
     cacheBuffers();
@@ -598,7 +647,7 @@
     sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
                             mDesiredPresentTime,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
-                            listenerCallbacks);
+                            hasListenerCallbacks, listenerCallbacks);
     mInputWindowCommands.clear();
     mStatus = NO_ERROR;
     return NO_ERROR;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index c84910b..06be1b3 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -140,7 +140,7 @@
                                      const sp<IBinder>& applyToken,
                                      const InputWindowCommands& inputWindowCommands,
                                      int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer,
+                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                      const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
 
     /* signal that we're done booting.
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2eb5492..a49ed52 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
 #ifndef NO_INPUT
@@ -123,7 +124,6 @@
             surfaceDamageRegion(),
             api(-1),
             colorTransform(mat4()),
-            hasListenerCallbacks(false),
             bgColorAlpha(0),
             bgColorDataspace(ui::Dataspace::UNKNOWN),
             colorSpaceAgnostic(false) {
@@ -186,7 +186,6 @@
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
 
-    bool hasListenerCallbacks;
 #ifndef NO_INPUT
     InputWindowInfo inputInfo;
 #endif
@@ -203,6 +202,8 @@
     // A color space agnostic layer means the color of this layer can be
     // interpreted in any color space.
     bool colorSpaceAgnostic;
+
+    std::vector<ListenerCallbacks> listeners;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 28f5a26..2527ec0 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -179,9 +179,6 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
-    nsecs_t getLastDequeueStartTime() const;
-
 protected:
     virtual ~Surface();
 
@@ -247,6 +244,8 @@
     int dispatchGetHdrSupport(va_list args);
     int dispatchGetConsumerUsage64(va_list args);
     int dispatchSetAutoPrerotation(va_list args);
+    int dispatchGetLastDequeueStartTime(va_list args);
+    int dispatchSetDequeueTimeout(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8a6920c..15287e2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -291,6 +291,7 @@
     };
 
     class Transaction : public Parcelable {
+    protected:
         std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index ab6dcaa..cbda6b2 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -47,7 +47,6 @@
         "libcutils",
         "libgui",
         "libhidlbase",
-        "libhidltransport",
         "libinput",
         "libui",
         "libutils",
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 386f731..2ab34da 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -435,6 +435,9 @@
     surface->expectTap(1, 1);
 }
 
+/**
+ * TODO(b/139494112) fix tests once we define expected behavior
+ *
 // Ensure we send the input to the right surface when the surface visibility changes due to the
 // first buffer being submitted. ref: b/120839715
 TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
@@ -486,6 +489,7 @@
     injectTap(11, 11);
     bgSurface->expectTap(1, 1);
 }
+*/
 
 TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5a121d7..a4fdb35 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -617,7 +617,7 @@
     anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
     nsecs_t after = systemTime(CLOCK_MONOTONIC);
 
-    nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+    nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get());
     ASSERT_LE(before, lastDequeueTime);
     ASSERT_GE(after, lastDequeueTime);
 }
@@ -688,6 +688,7 @@
                              const sp<IBinder>& /*applyToken*/,
                              const InputWindowCommands& /*inputWindowCommands*/,
                              int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
+                             bool /*hasListenerCallbacks*/,
                              const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
     }
 
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7835651..8a2fc2a 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -362,6 +362,13 @@
     if (!newFd.ok()) {
         ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
               strerror(errno));
+        const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+        // If this process is out of file descriptors, then throwing that might end up exploding
+        // on the other side of a binder call, which isn't really helpful.
+        // Better to just crash here and hope that the FD leak is slow.
+        // Other failures could be client errors, so we still propagate those back to the caller.
+        LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+                            getName().c_str());
         return nullptr;
     }
     return InputChannel::create(mName, std::move(newFd));
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 4c59e6c..fecfa19 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -274,3 +274,17 @@
 int ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
     return query(window, NATIVE_WINDOW_LAST_DEQUEUE_DURATION);
 }
+
+int ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+    return query(window, NATIVE_WINDOW_LAST_QUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+    int64_t time;
+    int success = window->perform(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START, &time);
+    return success < 0 ? success : time;
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+    return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
+}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 0260cbc..9798c2f 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -31,4 +31,30 @@
  */
 int ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
 
+/**
+ * Retrieves how long it took for the last time a buffer was queued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * microseconds
+ */
+int ANativeWindow_getLastQueueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves the system time in nanoseconds when the last time a buffer
+ * was dequeued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * nanoseconds.
+ */
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
+
+/**
+ * Sets a timeout in nanoseconds for dequeue calls. All subsequent dequeue calls
+ * made by the window will return -ETIMEDOUT after the timeout if the dequeue
+ * takes too long.
+ *
+ * \return NO_ERROR on succes, -errno on error.
+ */
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
+
 __END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 78354bb..8f85007 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -93,7 +93,6 @@
      */
     NATIVE_WINDOW_CONCRETE_TYPE = 5,
 
-
     /*
      * Default width and height of ANativeWindow buffers, these are the
      * dimensions of the window buffers irrespective of the
@@ -240,6 +239,8 @@
     NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA   = 33,
     NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
     NATIVE_WINDOW_SET_AUTO_PREROTATION            = 35,
+    NATIVE_WINDOW_GET_LAST_DEQUEUE_START          = 36,    /* private */
+    NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT             = 37,    /* private */
     // clang-format on
 };
 
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 7fe4df0..b741faa 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -23,6 +23,8 @@
     ANativeWindow_getFormat;
     ANativeWindow_getHeight;
     ANativeWindow_getLastDequeueDuration; # apex # introduced=30
+    ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
+    ANativeWindow_getLastQueueDuration; # apex # introduced=30
     ANativeWindow_getWidth;
     ANativeWindow_lock;
     ANativeWindow_query; # vndk
@@ -38,6 +40,7 @@
     ANativeWindow_setBuffersGeometry;
     ANativeWindow_setBuffersTimestamp; # vndk
     ANativeWindow_setBuffersTransform;
+    ANativeWindow_setDequeueTimeout; # apex # introduced=30
     ANativeWindow_setSharedBufferMode; # vndk
     ANativeWindow_setSwapInterval; # vndk
     ANativeWindow_setUsage; # vndk
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 5247e04..4d2b8c8 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -36,6 +36,12 @@
 
     // Exposes the internal last dequeue duration that's stored on the Surface.
     nsecs_t getLastDequeueDuration() const { return mLastDequeueDuration; }
+
+    // Exposes the internal last queue duration that's stored on the Surface.
+    nsecs_t getLastQueueDuration() const { return mLastQueueDuration; }
+
+    // Exposes the internal last dequeue start time that's stored on the Surface.
+    nsecs_t getLastDequeueStartTime() const { return mLastDequeueStartTime; }
 };
 
 class ANativeWindowTest : public ::testing::Test {
@@ -82,3 +88,81 @@
     EXPECT_GT(result, 0);
     EXPECT_EQ(result, mWindow->getLastDequeueDuration() / 1000);
 }
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) {
+    int result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noQueue_returnsZero) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_withQueue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_queueBuffer(mWindow.get(), buffer, 0);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration() / 1000);
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) {
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_withDequeue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastDequeueStartTime());
+}
+
+TEST_F(ANativeWindowTest, setDequeueTimeout_causesDequeueTimeout) {
+    nsecs_t timeout = milliseconds_to_nanoseconds(100);
+    int result = ANativeWindow_setDequeueTimeout(mWindow.get(), timeout);
+    EXPECT_EQ(0, result);
+
+    // The two dequeues should not timeout...
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+    int queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+    EXPECT_EQ(0, queueResult);
+    dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+    queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+    EXPECT_EQ(0, queueResult);
+
+    // ...but the third one should since the queue depth is too deep.
+    nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+    dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+    close(fd);
+    EXPECT_EQ(TIMED_OUT, dequeueResult);
+    EXPECT_GE(end - start, timeout);
+}
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index 81099e8..90c2330 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1,3 +1,3 @@
 arthuri@google.com
 bduddie@google.com
-bstack@google.com
+stange@google.com
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 96d5eb9..bf8b9f7 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -150,7 +150,7 @@
         class DeathObserver : public IBinder::DeathRecipient {
             SensorManager& mSensorManager;
             virtual void binderDied(const wp<IBinder>& who) {
-                ALOGW("sensorservice died [%p]", who.unsafe_get());
+                ALOGW("sensorservice died [%p]", static_cast<void*>(who.unsafe_get()));
                 mSensorManager.sensorManagerDied();
             }
         public:
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 8462fe7..47137f1 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -47,6 +47,7 @@
         "-Wno-padded",
 
         "-Wno-switch-enum",
+        "-Wno-format-pedantic",
     ],
 
     sanitize: {
@@ -99,8 +100,6 @@
         "libbase",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libsync",
         "libutils",
         "liblog",
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index c5170d0..0452f84 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,7 +38,6 @@
         "android.frameworks.bufferhub@1.0",
         "libcutils",
         "libhidlbase",
-        "libhwbinder",
         "libui",
         "libutils",
     ],
@@ -77,7 +76,6 @@
         "android.frameworks.bufferhub@1.0",
         "libcutils",
         "libhidlbase",
-        "libhwbinder",
         "liblog",
         "libui",
         "libutils"
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 4441672..2053344 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -57,7 +57,6 @@
     "libgui",
     "libsync",
     "libhidlbase",
-    "libhidltransport",
     "libfmq",
     "libpdx_default_transport",
 ]
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 5a9360c..582fed3 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -18,6 +18,8 @@
 #include <private/dvr/trusted_uids.h>
 #include <private/dvr/types.h>
 
+#include "DisplayHardware/DisplayIdentification.h"
+
 using android::dvr::display::DisplayProtocol;
 using android::pdx::Channel;
 using android::pdx::ErrorStatus;
@@ -44,19 +46,6 @@
            Endpoint::Create(display::DisplayProtocol::kClientPath)) {
     hardware_composer_.Initialize(
         hidl, primary_display_id, request_display_callback);
-
-    uint8_t port;
-    const auto error = hidl->getDisplayIdentificationData(
-        primary_display_id, &display_identification_port_,
-        &display_identification_data_);
-    if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
-      if (error !=
-          android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
-        ALOGI("DisplayService: identification data error\n");
-      } else {
-        ALOGI("DisplayService: identification data unsupported\n");
-      }
-    }
 }
 
 bool DisplayService::IsInitialized() const {
@@ -212,6 +201,7 @@
 pdx::Status<std::string> DisplayService::OnGetConfigurationData(
     pdx::Message& /*message*/, display::ConfigFileType config_type) {
   std::string property_name;
+  DisplayIdentificationData display_identification_data;
   switch (config_type) {
     case display::ConfigFileType::kLensMetrics:
       property_name = kDvrLensMetricsProperty;
@@ -223,11 +213,13 @@
       property_name = kDvrDeviceConfigProperty;
       break;
     case display::ConfigFileType::kDeviceEdid:
-      if (display_identification_data_.size() == 0) {
+      display_identification_data =
+          hardware_composer_.GetCurrentDisplayIdentificationData();
+      if (display_identification_data.size() == 0) {
         return ErrorStatus(ENOENT);
       }
-      return std::string(display_identification_data_.begin(),
-                         display_identification_data_.end());
+      return std::string(display_identification_data.begin(),
+                         display_identification_data.end());
     default:
       return ErrorStatus(EINVAL);
   }
@@ -246,7 +238,7 @@
 
 pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
     pdx::Message& /*message*/) {
-  return display_identification_port_;
+  return hardware_composer_.GetCurrentDisplayPort();
 }
 
 // Creates a new DisplaySurface and associates it with this channel. This may
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 06ba566..89f1eae 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -18,8 +18,6 @@
 #include "epoll_event_dispatcher.h"
 #include "hardware_composer.h"
 
-#include "DisplayHardware/DisplayIdentification.h"
-
 namespace android {
 namespace dvr {
 
@@ -120,9 +118,6 @@
 
   DisplayService(const DisplayService&) = delete;
   void operator=(const DisplayService&) = delete;
-
-  DisplayIdentificationData display_identification_data_;
-  uint8_t display_identification_port_;
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 9072d89..67607af 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -137,6 +137,20 @@
   composer_callback_->SetVsyncService(nullptr);
 }
 
+void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
+                                      hwc2_display_t hw_id) {
+  const auto error = composer->getDisplayIdentificationData(
+      hw_id, &display_port_, &display_identification_data_);
+  if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
+    if (error !=
+        android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
+      ALOGI("hardware_composer: identification data error\n");
+    } else {
+      ALOGI("hardware_composer: identification data unsupported\n");
+    }
+  }
+}
+
 bool HardwareComposer::Initialize(
     Hwc2::Composer* composer, hwc2_display_t primary_display_id,
     RequestDisplayCallback request_display_callback) {
@@ -164,6 +178,8 @@
       "HardwareComposer: Failed to create interrupt event fd : %s",
       strerror(errno));
 
+  UpdateEdidData(composer, primary_display_id);
+
   post_thread_ = std::thread(&HardwareComposer::PostThread, this);
 
   initialized_ = true;
@@ -988,6 +1004,9 @@
       EnableDisplay(*external_display_, false);
     }
 
+    // Update the cached edid data for the current display.
+    UpdateEdidData(composer_.get(), target_display_->id);
+
     // Turn the new target display on.
     EnableDisplay(*target_display_, true);
 
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index db0d6a7..989ce35 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -25,6 +25,7 @@
 #include <private/dvr/shared_buffer_helpers.h>
 #include <private/dvr/vsync_service.h>
 
+#include "DisplayHardware/DisplayIdentification.h"
 #include "acquired_buffer.h"
 #include "display_surface.h"
 
@@ -334,6 +335,14 @@
   int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
   void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
 
+  // Gets the edid data for the current active display (internal or external)
+  DisplayIdentificationData GetCurrentDisplayIdentificationData() {
+    return display_identification_data_;
+  }
+
+  // Gets the edid port for the current active display (internal or external)
+  uint8_t GetCurrentDisplayPort() { return display_port_; }
+
  private:
   DisplayParams GetDisplayParams(Hwc2::Composer* composer,
       hwc2_display_t display, bool is_primary);
@@ -544,6 +553,11 @@
   bool vsync_trace_parity_ = false;
   sp<VsyncService> vsync_service_;
 
+  // Edid section.
+  void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
+  DisplayIdentificationData display_identification_data_;
+  uint8_t display_port_;
+
   static constexpr int kPostThreadInterrupted = 1;
 
   HardwareComposer(const HardwareComposer&) = delete;
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 881f1b8..b505712 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,16 +1,7 @@
-# alanward@google.com
-chiur@google.com
 chrisforbes@google.com
 cnorthrop@google.com
 courtneygo@google.com
-hliatis@google.com
 ianelliott@google.com
 jessehall@google.com
 lpy@google.com
-marissaw@google.com
-nduca@google.com
-pmuetschard@google.com
-timvp@google.com
-tobine@google.com
-vhau@google.com
 zzyiwei@google.com
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 8144c8a..693e48e 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -153,7 +153,6 @@
         "android.hardware.configstore-utils",
         "libbase",
         "libhidlbase",
-        "libhidltransport",
         "libnativebridge_lazy",
         "libnativeloader_lazy",
         "libutils",
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index a9e873a..19c8b37 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -22,7 +22,6 @@
         "libbinder",
         "libgui",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
         "libnativewindow",
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index cd03bc2..2bb6aef 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -37,8 +37,6 @@
         "libcrypto",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libui",
         "libutils",
@@ -64,8 +62,6 @@
         "libcrypto",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libui",
         "libutils",
diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp
index 3442cb2..4d2d873 100644
--- a/services/displayservice/Android.bp
+++ b/services/displayservice/Android.bp
@@ -27,7 +27,6 @@
         "liblog",
         "libgui",
         "libhidlbase",
-        "libhidltransport",
         "libutils",
         "android.frameworks.displayservice@1.0",
     ],
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index bdee6fe..11578c3 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -30,7 +30,6 @@
     srcs: [
         "InputClassifier.cpp",
         "InputClassifierConverter.cpp",
-        "InputDispatcher.cpp",
         "InputManager.cpp",
     ],
 
@@ -50,6 +49,10 @@
         "server_configurable_flags",
     ],
 
+    static_libs: [
+        "libinputdispatcher",
+    ],
+
     cflags: [
         // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
@@ -60,43 +63,16 @@
         "include",
     ],
 
+    export_static_lib_headers: [
+        "libinputdispatcher",
+    ],
 }
 
 cc_library_headers {
     name: "libinputflinger_headers",
+    header_libs: ["libinputreporter_headers"],
     export_include_dirs: ["include"],
-}
-
-cc_library_shared {
-    name: "libinputreader",
-    defaults: ["inputflinger_defaults"],
-
-    srcs: [
-        "EventHub.cpp",
-        "InputReader.cpp",
-        "InputReaderFactory.cpp",
-        "TouchVideoDevice.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "libinputflinger_base",
-        "libcrypto",
-        "libcutils",
-        "libinput",
-        "liblog",
-        "libui",
-        "libutils",
-        "libhardware_legacy",
-    ],
-
-    header_libs: [
-        "libinputflinger_headers",
-    ],
-
-    export_header_lib_headers: [
-        "libinputflinger_headers",
-    ],
+    export_header_lib_headers: ["libinputreporter_headers"],
 }
 
 cc_library_shared {
@@ -124,28 +100,6 @@
     ],
 }
 
-cc_library_shared {
-    name: "libinputreporter",
-    defaults: ["inputflinger_defaults"],
-
-    srcs: [
-        "InputReporter.cpp",
-    ],
-
-    shared_libs: [
-        "liblog",
-        "libutils",
-    ],
-
-    header_libs: [
-        "libinputflinger_headers",
-    ],
-
-    export_header_lib_headers: [
-        "libinputflinger_headers",
-    ],
-}
-
 subdirs = [
     "host",
     "tests",
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
deleted file mode 100644
index 92e1e5f..0000000
--- a/services/inputflinger/InputDispatcher.h
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_DISPATCHER_H
-#define _UI_INPUT_DISPATCHER_H
-
-#include <condition_variable>
-#include <input/Input.h>
-#include <input/InputApplication.h>
-#include <input/InputTransport.h>
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-#include <optional>
-#include <ui/Region.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-#include <utils/BitSet.h>
-#include <cutils/atomic.h>
-#include <unordered_map>
-
-#include <limits.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <deque>
-#include <unordered_map>
-
-#include "InputListener.h"
-#include "InputReporterInterface.h"
-
-namespace android {
-
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
-    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
-    INPUT_EVENT_INJECTION_PENDING = -1,
-
-    /* Injection succeeded. */
-    INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
-    /* Injection failed because the injector did not have permission to inject
-     * into the application with input focus. */
-    INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
-    /* Injection failed because there were no available input targets. */
-    INPUT_EVENT_INJECTION_FAILED = 2,
-
-    /* Injection failed due to a timeout. */
-    INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
-    /* Injection is asynchronous and is assumed always to be successful. */
-    INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
-    /* Waits for previous events to be dispatched so that the input dispatcher can determine
-     * whether input event injection willbe permitted based on the current input focus.
-     * Does not wait for the input event to finish processing. */
-    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
-    /* Waits for the input event to be completely processed. */
-    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
-
-
-/*
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- */
-struct InputTarget {
-    enum {
-        /* This flag indicates that the event is being delivered to a foreground application. */
-        FLAG_FOREGROUND = 1 << 0,
-
-        /* This flag indicates that the MotionEvent falls within the area of the target
-         * obscured by another visible window above it.  The motion event should be
-         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
-        FLAG_WINDOW_IS_OBSCURED = 1 << 1,
-
-        /* This flag indicates that a motion event is being split across multiple windows. */
-        FLAG_SPLIT = 1 << 2,
-
-        /* This flag indicates that the pointer coordinates dispatched to the application
-         * will be zeroed out to avoid revealing information to an application. This is
-         * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
-         * the same UID from watching all touches. */
-        FLAG_ZERO_COORDS = 1 << 3,
-
-        /* This flag indicates that the event should be sent as is.
-         * Should always be set unless the event is to be transmuted. */
-        FLAG_DISPATCH_AS_IS = 1 << 8,
-
-        /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
-         * of the area of this target and so should instead be delivered as an
-         * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
-        FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
-
-        /* This flag indicates that a hover sequence is starting in the given window.
-         * The event is transmuted into ACTION_HOVER_ENTER. */
-        FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
-
-        /* This flag indicates that a hover event happened outside of a window which handled
-         * previous hover events, signifying the end of the current hover sequence for that
-         * window.
-         * The event is transmuted into ACTION_HOVER_ENTER. */
-        FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
-
-        /* This flag indicates that the event should be canceled.
-         * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
-         * outside of a window. */
-        FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
-
-        /* This flag indicates that the event should be dispatched as an initial down.
-         * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
-         * into a new window. */
-        FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
-
-        /* Mask for all dispatch modes. */
-        FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
-                | FLAG_DISPATCH_AS_OUTSIDE
-                | FLAG_DISPATCH_AS_HOVER_ENTER
-                | FLAG_DISPATCH_AS_HOVER_EXIT
-                | FLAG_DISPATCH_AS_SLIPPERY_EXIT
-                | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
-
-        /* This flag indicates that the target of a MotionEvent is partly or wholly
-         * obscured by another visible window above it.  The motion event should be
-         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
-        FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
-
-    };
-
-    // The input channel to be targeted.
-    sp<InputChannel> inputChannel;
-
-    // Flags for the input target.
-    int32_t flags;
-
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float xOffset, yOffset;
-
-    // Scaling factor to apply to MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float globalScaleFactor;
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
-
-    // The subset of pointer ids to include in motion events dispatched to this input target
-    // if FLAG_SPLIT is set.
-    BitSet32 pointerIds;
-};
-
-
-/*
- * Input dispatcher configuration.
- *
- * Specifies various options that modify the behavior of the input dispatcher.
- * The values provided here are merely defaults. The actual values will come from ViewConfiguration
- * and are passed into the dispatcher during initialization.
- */
-struct InputDispatcherConfiguration {
-    // The key repeat initial timeout.
-    nsecs_t keyRepeatTimeout;
-
-    // The key repeat inter-key delay.
-    nsecs_t keyRepeatDelay;
-
-    InputDispatcherConfiguration() :
-            keyRepeatTimeout(500 * 1000000LL),
-            keyRepeatDelay(50 * 1000000LL) { }
-};
-
-
-/*
- * Input dispatcher policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This interface is also mocked in the unit tests.
- */
-class InputDispatcherPolicyInterface : public virtual RefBase {
-protected:
-    InputDispatcherPolicyInterface() { }
-    virtual ~InputDispatcherPolicyInterface() { }
-
-public:
-    /* Notifies the system that a configuration change has occurred. */
-    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-
-    /* Notifies the system that an application is not responding.
-     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-            const sp<IBinder>& token,
-            const std::string& reason) = 0;
-
-    /* Notifies the system that an input channel is unrecoverably broken. */
-    virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
-    virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
-
-    /* Gets the input dispatcher configuration. */
-    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
-
-    /* Filters an input event.
-     * Return true to dispatch the event unmodified, false to consume the event.
-     * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
-     * to injectInputEvent.
-     */
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
-
-    /* Intercepts a key event immediately before queueing it.
-     * The policy can use this method as an opportunity to perform power management functions
-     * and early event preprocessing such as updating policy flags.
-     *
-     * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
-     * should be dispatched to applications.
-     */
-    virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
-
-    /* Intercepts a touch, trackball or other motion event before queueing it.
-     * The policy can use this method as an opportunity to perform power management functions
-     * and early event preprocessing such as updating policy flags.
-     *
-     * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
-     * should be dispatched to applications.
-     */
-    virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
-            uint32_t& policyFlags) = 0;
-
-    /* Allows the policy a chance to intercept a key before dispatching. */
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
-            const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
-
-    /* Allows the policy a chance to perform default processing for an unhandled key.
-     * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
-    virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
-            const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
-
-    /* Notifies the policy about switch events.
-     */
-    virtual void notifySwitch(nsecs_t when,
-            uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0;
-
-    /* Poke user activity for an event dispatched to a window. */
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
-
-    /* Checks whether a given application pid/uid has permission to inject input events
-     * into other applications.
-     *
-     * This method is special in that its implementation promises to be non-reentrant and
-     * is safe to call while holding other locks.  (Most other methods make no such guarantees!)
-     */
-    virtual bool checkInjectEventsPermissionNonReentrant(
-            int32_t injectorPid, int32_t injectorUid) = 0;
-
-    /* Notifies the policy that a pointer down event has occurred outside the current focused
-     * window.
-     *
-     * The touchedToken passed as an argument is the window that received the input event.
-     */
-    virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
-};
-
-
-/* Notifies the system about input events generated by the input reader.
- * The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
-protected:
-    InputDispatcherInterface() { }
-    virtual ~InputDispatcherInterface() { }
-
-public:
-    /* Dumps the state of the input dispatcher.
-     *
-     * This method may be called on any thread (usually by the input manager). */
-    virtual void dump(std::string& dump) = 0;
-
-    /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
-    virtual void monitor() = 0;
-
-    /* Runs a single iteration of the dispatch loop.
-     * Nominally processes one queued event, a timeout, or a response from an input consumer.
-     *
-     * This method should only be called on the input dispatcher thread.
-     */
-    virtual void dispatchOnce() = 0;
-
-    /* Injects an input event and optionally waits for sync.
-     * The synchronization mode determines whether the method blocks while waiting for
-     * input injection to proceed.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
-            uint32_t policyFlags) = 0;
-
-    /* Sets the list of input windows.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
-            int32_t displayId,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
-
-    /* Sets the focused application on the given display.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void setFocusedApplication(
-            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
-
-    /* Sets the focused display.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void setFocusedDisplay(int32_t displayId) = 0;
-
-    /* Sets the input dispatching mode.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
-
-    /* Sets whether input event filtering is enabled.
-     * When enabled, incoming input events are sent to the policy's filterInputEvent
-     * method instead of being dispatched.  The filter is expected to use
-     * injectInputEvent to inject the events it would like to have dispatched.
-     * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
-     */
-    virtual void setInputFilterEnabled(bool enabled) = 0;
-
-    /* Transfers touch focus from one window to another window.
-     *
-     * Returns true on success.  False if the window did not actually have touch focus.
-     */
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
-    /* Registers input channels that may be used as targets for input events.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual status_t registerInputChannel(
-            const sp<InputChannel>& inputChannel, int32_t displayId) = 0;
-
-    /* Registers input channels to be used to monitor input events.
-     *
-     * Each monitor must target a specific display and will only receive input events sent to that
-     * display. If the monitor is a gesture monitor, it will only receive pointer events on the
-     * targeted display.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual status_t registerInputMonitor(
-            const sp<InputChannel>& inputChannel, int32_t displayId, bool gestureMonitor) = 0;
-
-    /* Unregister input channels that will no longer receive input events.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
-    /* Allows an input monitor steal the current pointer stream away from normal input windows.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
-
-};
-
-/* Dispatches events to input targets.  Some functions of the input dispatcher, such as
- * identifying input targets, are controlled by a separate policy object.
- *
- * IMPORTANT INVARIANT:
- *     Because the policy can potentially block or cause re-entrance into the input dispatcher,
- *     the input dispatcher never calls into the policy while holding its internal locks.
- *     The implementation is also carefully designed to recover from scenarios such as an
- *     input channel becoming unregistered while identifying input targets or processing timeouts.
- *
- *     Methods marked 'Locked' must be called with the lock acquired.
- *
- *     Methods marked 'LockedInterruptible' must be called with the lock acquired but
- *     may during the course of their execution release the lock, call into the policy, and
- *     then reacquire the lock.  The caller is responsible for recovering gracefully.
- *
- *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
- */
-class InputDispatcher : public InputDispatcherInterface {
-protected:
-    virtual ~InputDispatcher();
-
-public:
-    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
-
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
-
-    virtual void dispatchOnce() override;
-
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
-            uint32_t policyFlags) override;
-
-    virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
-            int32_t displayId,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
-    virtual void setFocusedApplication(int32_t displayId,
-            const sp<InputApplicationHandle>& inputApplicationHandle) override;
-    virtual void setFocusedDisplay(int32_t displayId) override;
-    virtual void setInputDispatchMode(bool enabled, bool frozen) override;
-    virtual void setInputFilterEnabled(bool enabled) override;
-
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken)
-            override;
-
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            int32_t displayId) override;
-    virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel,
-            int32_t displayId, bool isGestureMonitor) override;
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
-    virtual status_t pilferPointers(const sp<IBinder>& token) override;
-
-private:
-
-    struct InjectionState {
-        mutable int32_t refCount;
-
-        int32_t injectorPid;
-        int32_t injectorUid;
-        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
-        bool injectionIsAsync; // set to true if injection is not waiting for the result
-        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
-
-        InjectionState(int32_t injectorPid, int32_t injectorUid);
-        void release();
-
-    private:
-        ~InjectionState();
-    };
-
-    struct EventEntry {
-        enum {
-            TYPE_CONFIGURATION_CHANGED,
-            TYPE_DEVICE_RESET,
-            TYPE_KEY,
-            TYPE_MOTION
-        };
-
-        uint32_t sequenceNum;
-        mutable int32_t refCount;
-        int32_t type;
-        nsecs_t eventTime;
-        uint32_t policyFlags;
-        InjectionState* injectionState;
-
-        bool dispatchInProgress; // initially false, set to true while dispatching
-
-        inline bool isInjected() const { return injectionState != nullptr; }
-
-        void release();
-
-        virtual void appendDescription(std::string& msg) const = 0;
-
-    protected:
-        EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
-        virtual ~EventEntry();
-        void releaseInjectionState();
-    };
-
-    struct ConfigurationChangedEntry : EventEntry {
-        explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
-        virtual void appendDescription(std::string& msg) const;
-
-    protected:
-        virtual ~ConfigurationChangedEntry();
-    };
-
-    struct DeviceResetEntry : EventEntry {
-        int32_t deviceId;
-
-        DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
-        virtual void appendDescription(std::string& msg) const;
-
-    protected:
-        virtual ~DeviceResetEntry();
-    };
-
-    struct KeyEntry : EventEntry {
-        int32_t deviceId;
-        uint32_t source;
-        int32_t displayId;
-        int32_t action;
-        int32_t flags;
-        int32_t keyCode;
-        int32_t scanCode;
-        int32_t metaState;
-        int32_t repeatCount;
-        nsecs_t downTime;
-
-        bool syntheticRepeat; // set to true for synthetic key repeats
-
-        enum InterceptKeyResult {
-            INTERCEPT_KEY_RESULT_UNKNOWN,
-            INTERCEPT_KEY_RESULT_SKIP,
-            INTERCEPT_KEY_RESULT_CONTINUE,
-            INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
-        };
-        InterceptKeyResult interceptKeyResult; // set based on the interception result
-        nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
-
-        KeyEntry(uint32_t sequenceNum, 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,
-                int32_t repeatCount, nsecs_t downTime);
-        virtual void appendDescription(std::string& msg) const;
-        void recycle();
-
-    protected:
-        virtual ~KeyEntry();
-    };
-
-    struct MotionEntry : EventEntry {
-        nsecs_t eventTime;
-        int32_t deviceId;
-        uint32_t source;
-        int32_t displayId;
-        int32_t action;
-        int32_t actionButton;
-        int32_t flags;
-        int32_t metaState;
-        int32_t buttonState;
-        MotionClassification classification;
-        int32_t edgeFlags;
-        float xPrecision;
-        float yPrecision;
-        float xCursorPosition;
-        float yCursorPosition;
-        nsecs_t downTime;
-        uint32_t pointerCount;
-        PointerProperties pointerProperties[MAX_POINTERS];
-        PointerCoords pointerCoords[MAX_POINTERS];
-
-        MotionEntry(uint32_t sequenceNum, 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, float xPrecision,
-                    float yPrecision, float xCursorPosition, float yCursorPosition,
-                    nsecs_t downTime, uint32_t pointerCount,
-                    const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-                    float xOffset, float yOffset);
-        virtual void appendDescription(std::string& msg) const;
-
-    protected:
-        virtual ~MotionEntry();
-    };
-
-    // Tracks the progress of dispatching a particular event to a particular connection.
-    struct DispatchEntry {
-        const uint32_t seq; // unique sequence number, never 0
-
-        EventEntry* eventEntry; // the event to dispatch
-        int32_t targetFlags;
-        float xOffset;
-        float yOffset;
-        float globalScaleFactor;
-        float windowXScale = 1.0f;
-        float windowYScale = 1.0f;
-        nsecs_t deliveryTime; // time when the event was actually delivered
-
-        // Set to the resolved action and flags when the event is enqueued.
-        int32_t resolvedAction;
-        int32_t resolvedFlags;
-
-        DispatchEntry(EventEntry* eventEntry,
-                int32_t targetFlags, float xOffset, float yOffset,
-                float globalScaleFactor, float windowXScale, float windowYScale);
-        ~DispatchEntry();
-
-        inline bool hasForegroundTarget() const {
-            return targetFlags & InputTarget::FLAG_FOREGROUND;
-        }
-
-        inline bool isSplit() const {
-            return targetFlags & InputTarget::FLAG_SPLIT;
-        }
-
-    private:
-        static volatile int32_t sNextSeqAtomic;
-
-        static uint32_t nextSeq();
-    };
-
-    // A command entry captures state and behavior for an action to be performed in the
-    // dispatch loop after the initial processing has taken place.  It is essentially
-    // a kind of continuation used to postpone sensitive policy interactions to a point
-    // in the dispatch loop where it is safe to release the lock (generally after finishing
-    // the critical parts of the dispatch cycle).
-    //
-    // The special thing about commands is that they can voluntarily release and reacquire
-    // the dispatcher lock at will.  Initially when the command starts running, the
-    // dispatcher lock is held.  However, if the command needs to call into the policy to
-    // do some work, it can release the lock, do the work, then reacquire the lock again
-    // before returning.
-    //
-    // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
-    // never calls into the policy while holding its lock.
-    //
-    // Commands are implicitly 'LockedInterruptible'.
-    struct CommandEntry;
-    typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
-
-    class Connection;
-    struct CommandEntry {
-        explicit CommandEntry(Command command);
-        ~CommandEntry();
-
-        Command command;
-
-        // parameters for the command (usage varies by command)
-        sp<Connection> connection;
-        nsecs_t eventTime;
-        KeyEntry* keyEntry;
-        sp<InputApplicationHandle> inputApplicationHandle;
-        std::string reason;
-        int32_t userActivityEventType;
-        uint32_t seq;
-        bool handled;
-        sp<InputChannel> inputChannel;
-        sp<IBinder> oldToken;
-        sp<IBinder> newToken;
-    };
-
-    /* Specifies which events are to be canceled and why. */
-    struct CancelationOptions {
-        enum Mode {
-            CANCEL_ALL_EVENTS = 0,
-            CANCEL_POINTER_EVENTS = 1,
-            CANCEL_NON_POINTER_EVENTS = 2,
-            CANCEL_FALLBACK_EVENTS = 3,
-        };
-
-        // The criterion to use to determine which events should be canceled.
-        Mode mode;
-
-        // Descriptive reason for the cancelation.
-        const char* reason;
-
-        // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
-        std::optional<int32_t> keyCode = std::nullopt;
-
-        // The specific device id of events to cancel, or nullopt to cancel events from any device.
-        std::optional<int32_t> deviceId = std::nullopt;
-
-        // The specific display id of events to cancel, or nullopt to cancel events on any display.
-        std::optional<int32_t> displayId = std::nullopt;
-
-        CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { }
-    };
-
-    /* Tracks dispatched key and motion event state so that cancelation events can be
-     * synthesized when events are dropped. */
-    class InputState {
-    public:
-        InputState();
-        ~InputState();
-
-        // Returns true if there is no state to be canceled.
-        bool isNeutral() const;
-
-        // Returns true if the specified source is known to have received a hover enter
-        // motion event.
-        bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
-
-        // Records tracking information for a key event that has just been published.
-        // Returns true if the event should be delivered, false if it is inconsistent
-        // and should be skipped.
-        bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
-
-        // Records tracking information for a motion event that has just been published.
-        // Returns true if the event should be delivered, false if it is inconsistent
-        // and should be skipped.
-        bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
-
-        // Synthesizes cancelation events for the current state and resets the tracked state.
-        void synthesizeCancelationEvents(nsecs_t currentTime,
-                std::vector<EventEntry*>& outEvents, const CancelationOptions& options);
-
-        // Clears the current state.
-        void clear();
-
-        // Copies pointer-related parts of the input state to another instance.
-        void copyPointerStateTo(InputState& other) const;
-
-        // Gets the fallback key associated with a keycode.
-        // Returns -1 if none.
-        // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
-        int32_t getFallbackKey(int32_t originalKeyCode);
-
-        // Sets the fallback key for a particular keycode.
-        void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
-
-        // Removes the fallback key for a particular keycode.
-        void removeFallbackKey(int32_t originalKeyCode);
-
-        inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const {
-            return mFallbackKeys;
-        }
-
-    private:
-        struct KeyMemento {
-            int32_t deviceId;
-            uint32_t source;
-            int32_t displayId;
-            int32_t keyCode;
-            int32_t scanCode;
-            int32_t metaState;
-            int32_t flags;
-            nsecs_t downTime;
-            uint32_t policyFlags;
-        };
-
-        struct MotionMemento {
-            int32_t deviceId;
-            uint32_t source;
-            int32_t displayId;
-            int32_t flags;
-            float xPrecision;
-            float yPrecision;
-            float xCursorPosition;
-            float yCursorPosition;
-            nsecs_t downTime;
-            uint32_t pointerCount;
-            PointerProperties pointerProperties[MAX_POINTERS];
-            PointerCoords pointerCoords[MAX_POINTERS];
-            bool hovering;
-            uint32_t policyFlags;
-
-            void setPointers(const MotionEntry* entry);
-        };
-
-        std::vector<KeyMemento> mKeyMementos;
-        std::vector<MotionMemento> mMotionMementos;
-        KeyedVector<int32_t, int32_t> mFallbackKeys;
-
-        ssize_t findKeyMemento(const KeyEntry* entry) const;
-        ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
-
-        void addKeyMemento(const KeyEntry* entry, int32_t flags);
-        void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
-
-        static bool shouldCancelKey(const KeyMemento& memento,
-                const CancelationOptions& options);
-        static bool shouldCancelMotion(const MotionMemento& memento,
-                const CancelationOptions& options);
-    };
-
-    /* Manages the dispatch state associated with a single input channel. */
-    class Connection : public RefBase {
-    protected:
-        virtual ~Connection();
-
-    public:
-        enum Status {
-            // Everything is peachy.
-            STATUS_NORMAL,
-            // An unrecoverable communication error has occurred.
-            STATUS_BROKEN,
-            // The input channel has been unregistered.
-            STATUS_ZOMBIE
-        };
-
-        Status status;
-        sp<InputChannel> inputChannel; // never null
-        bool monitor;
-        InputPublisher inputPublisher;
-        InputState inputState;
-
-        // True if the socket is full and no further events can be published until
-        // the application consumes some of the input.
-        bool inputPublisherBlocked;
-
-        // Queue of events that need to be published to the connection.
-        std::deque<DispatchEntry*> outboundQueue;
-
-        // Queue of events that have been published to the connection but that have not
-        // yet received a "finished" response from the application.
-        std::deque<DispatchEntry*> waitQueue;
-
-        explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
-
-        inline const std::string getInputChannelName() const { return inputChannel->getName(); }
-
-        const std::string getWindowName() const;
-        const char* getStatusLabel() const;
-
-        std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
-    };
-
-    struct Monitor {
-        sp<InputChannel> inputChannel; // never null
-
-        explicit Monitor(const sp<InputChannel>& inputChannel);
-    };
-
-    enum DropReason {
-        DROP_REASON_NOT_DROPPED = 0,
-        DROP_REASON_POLICY = 1,
-        DROP_REASON_APP_SWITCH = 2,
-        DROP_REASON_DISABLED = 3,
-        DROP_REASON_BLOCKED = 4,
-        DROP_REASON_STALE = 5,
-    };
-
-    sp<InputDispatcherPolicyInterface> mPolicy;
-    InputDispatcherConfiguration mConfig;
-
-    std::mutex mLock;
-
-    std::condition_variable mDispatcherIsAlive;
-
-    sp<Looper> mLooper;
-
-    EventEntry* mPendingEvent GUARDED_BY(mLock);
-    std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
-    std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
-    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
-
-    DropReason mLastDropReason GUARDED_BY(mLock);
-
-    void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
-
-    // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
-    bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
-
-    // Cleans up input state when dropping an inbound event.
-    void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
-
-    // Adds an event to a queue of recent events for debugging purposes.
-    void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
-
-    // App switch latency optimization.
-    bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
-    nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
-
-    bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
-    bool isAppSwitchPendingLocked() REQUIRES(mLock);
-    void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
-
-    // Stale event latency optimization.
-    static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
-
-    // Blocked event latency optimization.  Drops old events when the user intends
-    // to transfer focus to a new application.
-    EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
-
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
-            bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock);
-
-    // All registered connections mapped by channel file descriptor.
-    KeyedVector<int, sp<Connection> > mConnectionsByFd GUARDED_BY(mLock);
-
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& b) const {
-            return std::hash<IBinder *>{}(b.get());
-        }
-    };
-    std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
-            GUARDED_BY(mLock);
-
-    // Finds the display ID of the gesture monitor identified by the provided token.
-    std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
-            REQUIRES(mLock);
-
-    ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
-
-    // Input channels that will receive a copy of all input events sent to the provided display.
-    std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay
-            GUARDED_BY(mLock);
-
-    // Input channels that will receive pointer events that start within the corresponding display.
-    // These are a bit special when compared to global monitors since they'll cause gesture streams
-    // to continue even when there isn't a touched window,and have the ability to steal the rest of
-    // the pointer stream in order to claim it for a system gesture.
-    std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay
-            GUARDED_BY(mLock);
-
-
-    // Event injection and synchronization.
-    std::condition_variable mInjectionResultAvailable;
-    bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
-    void setInjectionResult(EventEntry* entry, int32_t injectionResult);
-
-    std::condition_variable mInjectionSyncFinished;
-    void incrementPendingForegroundDispatches(EventEntry* entry);
-    void decrementPendingForegroundDispatches(EventEntry* entry);
-
-    // Key repeat tracking.
-    struct KeyRepeatState {
-        KeyEntry* lastKeyEntry; // or null if no repeat
-        nsecs_t nextRepeatTime;
-    } mKeyRepeatState GUARDED_BY(mLock);
-
-    void resetKeyRepeatLocked() REQUIRES(mLock);
-    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
-
-    // Key replacement tracking
-    struct KeyReplacement {
-        int32_t keyCode;
-        int32_t deviceId;
-        bool operator==(const KeyReplacement& rhs) const {
-            return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
-        }
-        bool operator<(const KeyReplacement& rhs) const {
-            return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
-        }
-    };
-    // Maps the key code replaced, device id tuple to the key code it was replaced with
-    KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
-    // Process certain Meta + Key combinations
-    void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
-            int32_t& keyCode, int32_t& metaState);
-
-    // Deferred command processing.
-    bool haveCommandsLocked() const REQUIRES(mLock);
-    bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
-
-    // Input filter processing.
-    bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
-    bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
-
-    // Inbound event processing.
-    void drainInboundQueueLocked() REQUIRES(mLock);
-    void releasePendingEventLocked() REQUIRES(mLock);
-    void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
-
-    // Dispatch state.
-    bool mDispatchEnabled GUARDED_BY(mLock);
-    bool mDispatchFrozen GUARDED_BY(mLock);
-    bool mInputFilterEnabled GUARDED_BY(mLock);
-
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
-            GUARDED_BY(mLock);
-    // Get window handles by display, return an empty vector if not found.
-    std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
-            REQUIRES(mLock);
-    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
-            REQUIRES(mLock);
-    sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
-    bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
-
-    /*
-     * Validate and update InputWindowHandles for a given display.
-     */
-    void updateWindowHandlesForDisplayLocked(
-            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
-            REQUIRES(mLock);
-
-    // Focus tracking for keys, trackball, etc.
-    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
-            GUARDED_BY(mLock);
-
-    // Focus tracking for touch.
-    struct TouchedWindow {
-        sp<InputWindowHandle> windowHandle;
-        int32_t targetFlags;
-        BitSet32 pointerIds;        // zero unless target flag FLAG_SPLIT is set
-    };
-
-    // For tracking the offsets we need to apply when adding gesture monitor targets.
-    struct TouchedMonitor {
-        Monitor monitor;
-        float xOffset = 0.f;
-        float yOffset = 0.f;
-
-        explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
-    };
-
-    struct TouchState {
-        bool down;
-        bool split;
-        int32_t deviceId; // id of the device that is currently down, others are rejected
-        uint32_t source;  // source of the device that is current down, others are rejected
-        int32_t displayId; // id to the display that currently has a touch, others are rejected
-        std::vector<TouchedWindow> windows;
-
-        // This collects the portal windows that the touch has gone through. Each portal window
-        // targets a display (embedded display for most cases). With this info, we can add the
-        // monitoring channels of the displays touched.
-        std::vector<sp<InputWindowHandle>> portalWindows;
-
-        std::vector<TouchedMonitor> gestureMonitors;
-
-        TouchState();
-        ~TouchState();
-        void reset();
-        void copyFrom(const TouchState& other);
-        void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
-                int32_t targetFlags, BitSet32 pointerIds);
-        void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
-        void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
-        void removeWindow(const sp<InputWindowHandle>& windowHandle);
-        void removeWindowByToken(const sp<IBinder>& token);
-        void filterNonAsIsTouchWindows();
-        void filterNonMonitors();
-        sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
-        bool isSlippery() const;
-    };
-
-    KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
-    TouchState mTempTouchState GUARDED_BY(mLock);
-
-    // Focused applications.
-    std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
-            GUARDED_BY(mLock);
-
-    // Top focused display.
-    int32_t mFocusedDisplayId GUARDED_BY(mLock);
-
-    // Dispatcher state at time of last ANR.
-    std::string mLastANRState GUARDED_BY(mLock);
-
-    // Dispatch inbound events.
-    bool dispatchConfigurationChangedLocked(
-            nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock);
-    bool dispatchDeviceResetLocked(
-            nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
-    bool dispatchKeyLocked(
-            nsecs_t currentTime, KeyEntry* entry,
-            DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    bool dispatchMotionLocked(
-            nsecs_t currentTime, MotionEntry* entry,
-            DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
-            const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-
-    void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
-    void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
-
-    // Keeping track of ANR timeouts.
-    enum InputTargetWaitCause {
-        INPUT_TARGET_WAIT_CAUSE_NONE,
-        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
-        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
-    };
-
-    InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
-    bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
-    sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
-
-    // Contains the last window which received a hover event.
-    sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
-
-    // Finding targets for input events.
-    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
-            const sp<InputApplicationHandle>& applicationHandle,
-            const sp<InputWindowHandle>& windowHandle,
-            nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock);
-
-    void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
-    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
-            const sp<InputChannel>& inputChannel) REQUIRES(mLock);
-    nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
-    void resetANRTimeoutsLocked() REQUIRES(mLock);
-
-    int32_t getTargetDisplayId(const EventEntry* entry);
-    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
-            std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
-            std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
-            bool* outConflictingPointerActions) REQUIRES(mLock);
-    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId,
-            const std::vector<sp<InputWindowHandle>>& portalWindows) REQUIRES(mLock);
-    void addGestureMonitors(const std::vector<Monitor>& monitors,
-            std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, float yOffset = 0);
-
-    void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
-            int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
-            REQUIRES(mLock);
-    void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
-            std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
-            int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
-    void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
-    bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
-            const InjectionState* injectionState);
-    bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
-            int32_t x, int32_t y) const REQUIRES(mLock);
-    bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
-    std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
-            const sp<InputWindowHandle>& windowHandle);
-
-    std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
-            const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
-            const char* targetType) REQUIRES(mLock);
-
-    // Manage the dispatch cycle for a single connection.
-    // These methods are deliberately not Interruptible because doing all of the work
-    // with the mutex held makes it easier to ensure that connection invariants are maintained.
-    // If needed, the methods post commands to run later once the critical bits are done.
-    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
-    void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
-    void enqueueDispatchEntryLocked(const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
-            REQUIRES(mLock);
-    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
-            REQUIRES(mLock);
-    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            uint32_t seq, bool handled) REQUIRES(mLock);
-    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            bool notify) REQUIRES(mLock);
-    void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
-    void releaseDispatchEntry(DispatchEntry* dispatchEntry);
-    static int handleReceiveCallback(int fd, int events, void* data);
-    // The action sent should only be of type AMOTION_EVENT_*
-    void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
-            const sp<IBinder>& newToken) REQUIRES(mLock);
-
-    void synthesizeCancelationEventsForAllConnectionsLocked(
-            const CancelationOptions& options) REQUIRES(mLock);
-    void synthesizeCancelationEventsForMonitorsLocked(
-            const CancelationOptions& options) REQUIRES(mLock);
-    void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options,
-            std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
-    void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
-            const CancelationOptions& options) REQUIRES(mLock);
-    void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
-            const CancelationOptions& options) REQUIRES(mLock);
-
-    // Splitting motion events across windows.
-    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
-
-    // Reset and drop everything the dispatcher is doing.
-    void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
-
-    // Dump state.
-    void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
-    void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
-    void logDispatchStateLocked() REQUIRES(mLock);
-
-    // Registration.
-    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
-    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
-        std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay)
-            REQUIRES(mLock);
-    status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
-            REQUIRES(mLock);
-
-    // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleFinishedLocked(
-            nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled)
-             REQUIRES(mLock);
-    void onDispatchCycleBrokenLocked(
-            nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock);
-    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-            const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
-    void onANRLocked(
-            nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
-            const sp<InputWindowHandle>& windowHandle,
-            nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
-
-    // Outbound policy interactions.
-    void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
-            DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
-            DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
-    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
-    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-
-    // Statistics gathering.
-    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
-            int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
-    void traceInboundQueueLengthLocked() REQUIRES(mLock);
-    void traceOutboundQueueLength(const sp<Connection>& connection);
-    void traceWaitQueueLength(const sp<Connection>& connection);
-
-    sp<InputReporterInterface> mReporter;
-};
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
-    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
-    ~InputDispatcherThread();
-
-private:
-    virtual bool threadLoop();
-
-    sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3996cca..359325f 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -19,6 +19,8 @@
 //#define LOG_NDEBUG 0
 
 #include "InputManager.h"
+#include "InputDispatcherFactory.h"
+#include "InputDispatcherThread.h"
 #include "InputReaderFactory.h"
 
 #include <binder/IPCThreadState.h>
@@ -33,7 +35,7 @@
 InputManager::InputManager(
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
-    mDispatcher = new InputDispatcher(dispatcherPolicy);
+    mDispatcher = createInputDispatcher(dispatcherPolicy);
     mClassifier = new InputClassifier(mDispatcher);
     mReader = createInputReader(readerPolicy, mClassifier);
     initialize();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index e568df5..f3da324 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -21,15 +21,14 @@
  * Native input manager.
  */
 
-#include "EventHub.h"
-#include "InputReaderBase.h"
 #include "InputClassifier.h"
-#include "InputDispatcher.h"
-#include "InputReader.h"
+#include "InputReaderBase.h"
 
+#include <InputDispatcherInterface.h>
+#include <InputDispatcherPolicyInterface.h>
+#include <input/ISetInputWindowsListener.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
-#include <input/ISetInputWindowsListener.h>
 
 #include <input/IInputFlinger.h>
 #include <utils/Errors.h>
@@ -39,6 +38,7 @@
 
 namespace android {
 class InputChannel;
+class InputDispatcherThread;
 
 /*
  * The input manager is the core of the system event processing.
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
deleted file mode 100644
index 1cbf78e..0000000
--- a/services/inputflinger/InputReader.cpp
+++ /dev/null
@@ -1,7564 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputReader"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
-
-// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
-
-// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
-
-// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
-
-// Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
-
-// Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
-
-// Log debug messages about fusing stylus data.
-#define DEBUG_STYLUS_FUSION 0
-
-#include "InputReader.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <math.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include <android-base/stringprintf.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
-#define INDENT "  "
-#define INDENT2 "    "
-#define INDENT3 "      "
-#define INDENT4 "        "
-#define INDENT5 "          "
-
-using android::base::StringPrintf;
-
-namespace android {
-
-// --- Constants ---
-
-// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static constexpr size_t MAX_SLOTS = 32;
-
-// Maximum amount of latency to add to touch events while waiting for data from an
-// external stylus.
-static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
-
-// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
-
-// Artificial latency on synthetic events created from stylus data without corresponding touch
-// data.
-static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
-
-// --- Static Functions ---
-
-template<typename T>
-inline static T abs(const T& value) {
-    return value < 0 ? - value : value;
-}
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
-    return a < b ? a : b;
-}
-
-template<typename T>
-inline static void swap(T& a, T& b) {
-    T temp = a;
-    a = b;
-    b = temp;
-}
-
-inline static float avg(float x, float y) {
-    return (x + y) / 2;
-}
-
-inline static float distance(float x1, float y1, float x2, float y2) {
-    return hypotf(x1 - x2, y1 - y2);
-}
-
-inline static int32_t signExtendNybble(int32_t value) {
-    return value >= 8 ? value - 16 : value;
-}
-
-static inline const char* toString(bool value) {
-    return value ? "true" : "false";
-}
-
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
-        const int32_t map[][4], size_t mapSize) {
-    if (orientation != DISPLAY_ORIENTATION_0) {
-        for (size_t i = 0; i < mapSize; i++) {
-            if (value == map[i][0]) {
-                return map[i][orientation];
-            }
-        }
-    }
-    return value;
-}
-
-static const int32_t keyCodeRotationMap[][4] = {
-        // key codes enumerated counter-clockwise with the original (unrotated) key first
-        // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
-        { AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT },
-        { AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN },
-        { AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT },
-        { AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP },
-        { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
-            AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT },
-        { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
-            AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN },
-        { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
-            AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT },
-        { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
-            AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP },
-};
-static const size_t keyCodeRotationMapSize =
-        sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateStemKey(int32_t value, int32_t orientation,
-        const int32_t map[][2], size_t mapSize) {
-    if (orientation == DISPLAY_ORIENTATION_180) {
-        for (size_t i = 0; i < mapSize; i++) {
-            if (value == map[i][0]) {
-                return map[i][1];
-            }
-        }
-    }
-    return value;
-}
-
-// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
-static int32_t stemKeyRotationMap[][2] = {
-        // key codes enumerated with the original (unrotated) key first
-        // no rotation,           180 degree rotation
-        { AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY },
-        { AKEYCODE_STEM_1,       AKEYCODE_STEM_1 },
-        { AKEYCODE_STEM_2,       AKEYCODE_STEM_2 },
-        { AKEYCODE_STEM_3,       AKEYCODE_STEM_3 },
-};
-static const size_t stemKeyRotationMapSize =
-        sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    keyCode = rotateStemKey(keyCode, orientation,
-            stemKeyRotationMap, stemKeyRotationMapSize);
-    return rotateValueUsingRotationMap(keyCode, orientation,
-            keyCodeRotationMap, keyCodeRotationMapSize);
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
-    float temp;
-    switch (orientation) {
-    case DISPLAY_ORIENTATION_90:
-        temp = *deltaX;
-        *deltaX = *deltaY;
-        *deltaY = -temp;
-        break;
-
-    case DISPLAY_ORIENTATION_180:
-        *deltaX = -*deltaX;
-        *deltaY = -*deltaY;
-        break;
-
-    case DISPLAY_ORIENTATION_270:
-        temp = *deltaX;
-        *deltaX = -*deltaY;
-        *deltaY = temp;
-        break;
-    }
-}
-
-static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
-    return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
-}
-
-// Returns true if the pointer should be reported as being down given the specified
-// button states.  This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
-    return buttonState &
-            (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY
-                    | AMOTION_EVENT_BUTTON_TERTIARY);
-}
-
-static float calculateCommonVector(float a, float b) {
-    if (a > 0 && b > 0) {
-        return a < b ? a : b;
-    } else if (a < 0 && b < 0) {
-        return a > b ? a : b;
-    } else {
-        return 0;
-    }
-}
-
-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, 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->getNextSequenceNum(), when, 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, int32_t currentButtonState) {
-    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
-            lastButtonState, currentButtonState,
-            AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
-    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
-            lastButtonState, currentButtonState,
-            AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
-}
-
-
-// --- InputReader ---
-
-InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
-                         const sp<InputReaderPolicyInterface>& policy,
-                         const sp<InputListenerInterface>& listener)
-      : mContext(this),
-        mEventHub(eventHub),
-        mPolicy(policy),
-        mNextSequenceNum(1),
-        mGlobalMetaState(0),
-        mGeneration(1),
-        mDisableVirtualKeysTimeout(LLONG_MIN),
-        mNextTimeout(LLONG_MAX),
-        mConfigurationChangesToRefresh(0) {
-    mQueuedListener = new QueuedInputListener(listener);
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        refreshConfigurationLocked(0);
-        updateGlobalMetaStateLocked();
-    } // release lock
-}
-
-InputReader::~InputReader() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        delete mDevices.valueAt(i);
-    }
-}
-
-void InputReader::loopOnce() {
-    int32_t oldGeneration;
-    int32_t timeoutMillis;
-    bool inputDevicesChanged = false;
-    std::vector<InputDeviceInfo> inputDevices;
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        oldGeneration = mGeneration;
-        timeoutMillis = -1;
-
-        uint32_t changes = mConfigurationChangesToRefresh;
-        if (changes) {
-            mConfigurationChangesToRefresh = 0;
-            timeoutMillis = 0;
-            refreshConfigurationLocked(changes);
-        } else if (mNextTimeout != LLONG_MAX) {
-            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
-        }
-    } // release lock
-
-    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-        mReaderIsAliveCondition.broadcast();
-
-        if (count) {
-            processEventsLocked(mEventBuffer, count);
-        }
-
-        if (mNextTimeout != LLONG_MAX) {
-            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-            if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
-                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
-                mNextTimeout = LLONG_MAX;
-                timeoutExpiredLocked(now);
-            }
-        }
-
-        if (oldGeneration != mGeneration) {
-            inputDevicesChanged = true;
-            getInputDevicesLocked(inputDevices);
-        }
-    } // release lock
-
-    // Send out a message that the describes the changed input devices.
-    if (inputDevicesChanged) {
-        mPolicy->notifyInputDevicesChanged(inputDevices);
-    }
-
-    // Flush queued events out to the listener.
-    // This must happen outside of the lock because the listener could potentially call
-    // back into the InputReader's methods, such as getScanCodeState, or become blocked
-    // on another thread similarly waiting to acquire the InputReader lock thereby
-    // resulting in a deadlock.  This situation is actually quite plausible because the
-    // listener is actually the input dispatcher, which calls into the window manager,
-    // which occasionally calls into the input reader.
-    mQueuedListener->flush();
-}
-
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
-    for (const RawEvent* rawEvent = rawEvents; count;) {
-        int32_t type = rawEvent->type;
-        size_t batchSize = 1;
-        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
-            int32_t deviceId = rawEvent->deviceId;
-            while (batchSize < count) {
-                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
-                        || rawEvent[batchSize].deviceId != deviceId) {
-                    break;
-                }
-                batchSize += 1;
-            }
-#if DEBUG_RAW_EVENTS
-            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-#endif
-            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
-        } else {
-            switch (rawEvent->type) {
-            case EventHubInterface::DEVICE_ADDED:
-                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
-                break;
-            case EventHubInterface::DEVICE_REMOVED:
-                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
-                break;
-            case EventHubInterface::FINISHED_DEVICE_SCAN:
-                handleConfigurationChangedLocked(rawEvent->when);
-                break;
-            default:
-                ALOG_ASSERT(false); // can't happen
-                break;
-            }
-        }
-        count -= batchSize;
-        rawEvent += batchSize;
-    }
-}
-
-void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
-        return;
-    }
-
-    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
-    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
-    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
-
-    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
-    device->configure(when, &mConfig, 0);
-    device->reset(when);
-
-    if (device->isIgnored()) {
-        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
-                identifier.name.c_str());
-    } else {
-        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
-                identifier.name.c_str(), device->getSources());
-    }
-
-    mDevices.add(deviceId, device);
-    bumpGenerationLocked();
-
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        notifyExternalStylusPresenceChanged();
-    }
-}
-
-void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    InputDevice* device = nullptr;
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
-        return;
-    }
-
-    device = mDevices.valueAt(deviceIndex);
-    mDevices.removeItemsAt(deviceIndex, 1);
-    bumpGenerationLocked();
-
-    if (device->isIgnored()) {
-        ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
-                device->getId(), device->getName().c_str());
-    } else {
-        ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
-                device->getId(), device->getName().c_str(), device->getSources());
-    }
-
-    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        notifyExternalStylusPresenceChanged();
-    }
-
-    device->reset(when);
-    delete device;
-}
-
-InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-        const InputDeviceIdentifier& identifier, uint32_t classes) {
-    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
-            controllerNumber, identifier, classes);
-
-    // External devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
-        device->setExternal(true);
-    }
-
-    // Devices with mics.
-    if (classes & INPUT_DEVICE_CLASS_MIC) {
-        device->setMic(true);
-    }
-
-    // Switch-like devices.
-    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
-        device->addMapper(new SwitchInputMapper(device));
-    }
-
-    // Scroll wheel-like devices.
-    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
-        device->addMapper(new RotaryEncoderInputMapper(device));
-    }
-
-    // Vibrator-like devices.
-    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
-        device->addMapper(new VibratorInputMapper(device));
-    }
-
-    // Keyboard-like devices.
-    uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
-    if (classes & INPUT_DEVICE_CLASS_DPAD) {
-        keyboardSource |= AINPUT_SOURCE_DPAD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
-    }
-
-    if (keyboardSource != 0) {
-        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
-    }
-
-    // Cursor-like devices.
-    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
-        device->addMapper(new CursorInputMapper(device));
-    }
-
-    // Touchscreens and touchpad devices.
-    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
-        device->addMapper(new MultiTouchInputMapper(device));
-    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
-        device->addMapper(new SingleTouchInputMapper(device));
-    }
-
-    // Joystick-like devices.
-    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
-        device->addMapper(new JoystickInputMapper(device));
-    }
-
-    // External stylus-like devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        device->addMapper(new ExternalStylusInputMapper(device));
-    }
-
-    return device;
-}
-
-void InputReader::processEventsForDeviceLocked(int32_t deviceId,
-        const RawEvent* rawEvents, size_t count) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
-        return;
-    }
-
-    InputDevice* device = mDevices.valueAt(deviceIndex);
-    if (device->isIgnored()) {
-        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
-        return;
-    }
-
-    device->process(rawEvents, count);
-}
-
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        if (!device->isIgnored()) {
-            device->timeoutExpired(when);
-        }
-    }
-}
-
-void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
-    // Reset global meta state because it depends on the list of all configured devices.
-    updateGlobalMetaStateLocked();
-
-    // Enqueue configuration changed.
-    NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
-    mQueuedListener->notifyConfigurationChanged(&args);
-}
-
-void InputReader::refreshConfigurationLocked(uint32_t changes) {
-    mPolicy->getReaderConfiguration(&mConfig);
-    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
-
-    if (changes) {
-        ALOGI("Reconfiguring input devices, changes=%s",
-              InputReaderConfiguration::changesToString(changes).c_str());
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-
-        if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
-            mEventHub->requestReopenDevices();
-        } else {
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
-                device->configure(now, &mConfig, changes);
-            }
-        }
-    }
-}
-
-void InputReader::updateGlobalMetaStateLocked() {
-    mGlobalMetaState = 0;
-
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        mGlobalMetaState |= device->getMetaState();
-    }
-}
-
-int32_t InputReader::getGlobalMetaStateLocked() {
-    return mGlobalMetaState;
-}
-
-void InputReader::notifyExternalStylusPresenceChanged() {
-    refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
-}
-
-void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
-            InputDeviceInfo info;
-            device->getDeviceInfo(&info);
-            outDevices.push_back(info);
-        }
-    }
-}
-
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        device->updateExternalStylusState(state);
-    }
-}
-
-void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
-    mDisableVirtualKeysTimeout = time;
-}
-
-bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now,
-        InputDevice* device, int32_t keyCode, int32_t scanCode) {
-    if (now < mDisableVirtualKeysTimeout) {
-        ALOGI("Dropping virtual key from device %s because virtual keys are "
-                "temporarily disabled for the next %0.3fms.  keyCode=%d, scanCode=%d",
-                device->getName().c_str(),
-                (mDisableVirtualKeysTimeout - now) * 0.000001,
-                keyCode, scanCode);
-        return true;
-    } else {
-        return false;
-    }
-}
-
-void InputReader::fadePointerLocked() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        device->fadePointer();
-    }
-}
-
-void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
-    if (when < mNextTimeout) {
-        mNextTimeout = when;
-        mEventHub->wake();
-    }
-}
-
-int32_t InputReader::bumpGenerationLocked() {
-    return ++mGeneration;
-}
-
-void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
-    AutoMutex _l(mLock);
-    getInputDevicesLocked(outInputDevices);
-}
-
-void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
-    outInputDevices.clear();
-
-    size_t numDevices = mDevices.size();
-    for (size_t i = 0; i < numDevices; i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        if (!device->isIgnored()) {
-            InputDeviceInfo info;
-            device->getDeviceInfo(&info);
-            outInputDevices.push_back(info);
-        }
-    }
-}
-
-int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t keyCode) {
-    AutoMutex _l(mLock);
-
-    return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
-}
-
-int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t scanCode) {
-    AutoMutex _l(mLock);
-
-    return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
-}
-
-int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
-    AutoMutex _l(mLock);
-
-    return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
-}
-
-int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
-        GetStateFunc getStateFunc) {
-    int32_t result = AKEY_STATE_UNKNOWN;
-    if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
-            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = (device->*getStateFunc)(sourceMask, code);
-            }
-        }
-    } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
-                // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
-                int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
-                if (currentResult >= AKEY_STATE_DOWN) {
-                    return currentResult;
-                } else if (currentResult == AKEY_STATE_UP) {
-                    result = currentResult;
-                }
-            }
-        }
-    }
-    return result;
-}
-
-void InputReader::toggleCapsLockState(int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
-        return;
-    }
-
-    InputDevice* device = mDevices.valueAt(deviceIndex);
-    if (device->isIgnored()) {
-        return;
-    }
-
-    device->updateMetaState(AKEYCODE_CAPS_LOCK);
-}
-
-bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
-        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
-    AutoMutex _l(mLock);
-
-    memset(outFlags, 0, numCodes);
-    return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
-}
-
-bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
-        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
-    bool result = false;
-    if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
-            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = device->markSupportedKeyCodes(sourceMask,
-                        numCodes, keyCodes, outFlags);
-            }
-        }
-    } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result |= device->markSupportedKeyCodes(sourceMask,
-                        numCodes, keyCodes, outFlags);
-            }
-        }
-    }
-    return result;
-}
-
-void InputReader::requestRefreshConfiguration(uint32_t changes) {
-    AutoMutex _l(mLock);
-
-    if (changes) {
-        bool needWake = !mConfigurationChangesToRefresh;
-        mConfigurationChangesToRefresh |= changes;
-
-        if (needWake) {
-            mEventHub->wake();
-        }
-    }
-}
-
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-        ssize_t repeat, int32_t token) {
-    AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
-        device->vibrate(pattern, patternSize, repeat, token);
-    }
-}
-
-void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
-    AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
-        device->cancelVibrate(token);
-    }
-}
-
-bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
-    AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
-        return device->isEnabled();
-    }
-    ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
-    return false;
-}
-
-bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
-    AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
-        return false;
-    }
-
-    InputDevice* device = mDevices.valueAt(deviceIndex);
-    if (!device->isEnabled()) {
-        ALOGW("Ignoring disabled device %s", device->getName().c_str());
-        return false;
-    }
-
-    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
-    // No associated display. By default, can dispatch to all displays.
-    if (!associatedDisplayId) {
-        return true;
-    }
-
-    if (*associatedDisplayId == ADISPLAY_ID_NONE) {
-        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
-        return true;
-    }
-
-    return *associatedDisplayId == displayId;
-}
-
-void InputReader::dump(std::string& dump) {
-    AutoMutex _l(mLock);
-
-    mEventHub->dump(dump);
-    dump += "\n";
-
-    dump += "Input Reader State:\n";
-
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        mDevices.valueAt(i)->dump(dump);
-    }
-
-    dump += INDENT "Configuration:\n";
-    dump += INDENT2 "ExcludedDeviceNames: [";
-    for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
-        if (i != 0) {
-            dump += ", ";
-        }
-        dump += mConfig.excludedDeviceNames[i];
-    }
-    dump += "]\n";
-    dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
-            mConfig.virtualKeyQuietTime * 0.000001f);
-
-    dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: "
-            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
-            mConfig.pointerVelocityControlParameters.scale,
-            mConfig.pointerVelocityControlParameters.lowThreshold,
-            mConfig.pointerVelocityControlParameters.highThreshold,
-            mConfig.pointerVelocityControlParameters.acceleration);
-
-    dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: "
-            "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
-            mConfig.wheelVelocityControlParameters.scale,
-            mConfig.wheelVelocityControlParameters.lowThreshold,
-            mConfig.wheelVelocityControlParameters.highThreshold,
-            mConfig.wheelVelocityControlParameters.acceleration);
-
-    dump += StringPrintf(INDENT2 "PointerGesture:\n");
-    dump += StringPrintf(INDENT3 "Enabled: %s\n",
-            toString(mConfig.pointerGesturesEnabled));
-    dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n",
-            mConfig.pointerGestureQuietInterval * 0.000001f);
-    dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
-            mConfig.pointerGestureDragMinSwitchSpeed);
-    dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n",
-            mConfig.pointerGestureTapInterval * 0.000001f);
-    dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n",
-            mConfig.pointerGestureTapDragInterval * 0.000001f);
-    dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n",
-            mConfig.pointerGestureTapSlop);
-    dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
-            mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
-    dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
-            mConfig.pointerGestureMultitouchMinDistance);
-    dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
-            mConfig.pointerGestureSwipeTransitionAngleCosine);
-    dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
-            mConfig.pointerGestureSwipeMaxWidthRatio);
-    dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n",
-            mConfig.pointerGestureMovementSpeedRatio);
-    dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n",
-            mConfig.pointerGestureZoomSpeedRatio);
-
-    dump += INDENT3 "Viewports:\n";
-    mConfig.dump(dump);
-}
-
-void InputReader::monitor() {
-    // Acquire and release the lock to ensure that the reader has not deadlocked.
-    mLock.lock();
-    mEventHub->wake();
-    mReaderIsAliveCondition.wait(mLock);
-    mLock.unlock();
-
-    // Check the EventHub
-    mEventHub->monitor();
-}
-
-
-// --- InputReader::ContextImpl ---
-
-InputReader::ContextImpl::ContextImpl(InputReader* reader) :
-        mReader(reader) {
-}
-
-void InputReader::ContextImpl::updateGlobalMetaState() {
-    // lock is already held by the input loop
-    mReader->updateGlobalMetaStateLocked();
-}
-
-int32_t InputReader::ContextImpl::getGlobalMetaState() {
-    // lock is already held by the input loop
-    return mReader->getGlobalMetaStateLocked();
-}
-
-void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
-    // lock is already held by the input loop
-    mReader->disableVirtualKeysUntilLocked(time);
-}
-
-bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now,
-        InputDevice* device, int32_t keyCode, int32_t scanCode) {
-    // lock is already held by the input loop
-    return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
-}
-
-void InputReader::ContextImpl::fadePointer() {
-    // lock is already held by the input loop
-    mReader->fadePointerLocked();
-}
-
-void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
-    // lock is already held by the input loop
-    mReader->requestTimeoutAtTimeLocked(when);
-}
-
-int32_t InputReader::ContextImpl::bumpGeneration() {
-    // lock is already held by the input loop
-    return mReader->bumpGenerationLocked();
-}
-
-void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
-    // lock is already held by whatever called refreshConfigurationLocked
-    mReader->getExternalStylusDevicesLocked(outDevices);
-}
-
-void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
-    mReader->dispatchExternalStylusState(state);
-}
-
-InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
-    return mReader->mPolicy.get();
-}
-
-InputListenerInterface* InputReader::ContextImpl::getListener() {
-    return mReader->mQueuedListener.get();
-}
-
-EventHubInterface* InputReader::ContextImpl::getEventHub() {
-    return mReader->mEventHub.get();
-}
-
-uint32_t InputReader::ContextImpl::getNextSequenceNum() {
-    return (mReader->mNextSequenceNum)++;
-}
-
-// --- InputDevice ---
-
-InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
-        int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) :
-        mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber),
-        mIdentifier(identifier), mClasses(classes),
-        mSources(0), mIsExternal(false), mHasMic(false), mDropUntilNextSync(false) {
-}
-
-InputDevice::~InputDevice() {
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        delete mMappers[i];
-    }
-    mMappers.clear();
-}
-
-bool InputDevice::isEnabled() {
-    return getEventHub()->isDeviceEnabled(mId);
-}
-
-void InputDevice::setEnabled(bool enabled, nsecs_t when) {
-    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
-        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
-              "but the corresponding viewport is not found",
-              getName().c_str(), *mAssociatedDisplayPort);
-        enabled = false;
-    }
-
-    if (isEnabled() == enabled) {
-        return;
-    }
-
-    if (enabled) {
-        getEventHub()->enableDevice(mId);
-        reset(when);
-    } else {
-        reset(when);
-        getEventHub()->disableDevice(mId);
-    }
-    // Must change generation to flag this device as changed
-    bumpGeneration();
-}
-
-void InputDevice::dump(std::string& dump) {
-    InputDeviceInfo deviceInfo;
-    getDeviceInfo(&deviceInfo);
-
-    dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
-            deviceInfo.getDisplayName().c_str());
-    dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
-    dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
-    dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
-    if (mAssociatedDisplayPort) {
-        dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
-    } else {
-        dump += "<none>\n";
-    }
-    dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
-    dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
-    dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
-
-    const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
-    if (!ranges.empty()) {
-        dump += INDENT2 "Motion Ranges:\n";
-        for (size_t i = 0; i < ranges.size(); i++) {
-            const InputDeviceInfo::MotionRange& range = ranges[i];
-            const char* label = getAxisLabel(range.axis);
-            char name[32];
-            if (label) {
-                strncpy(name, label, sizeof(name));
-                name[sizeof(name) - 1] = '\0';
-            } else {
-                snprintf(name, sizeof(name), "%d", range.axis);
-            }
-            dump += StringPrintf(INDENT3 "%s: source=0x%08x, "
-                    "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
-                    name, range.source, range.min, range.max, range.flat, range.fuzz,
-                    range.resolution);
-        }
-    }
-
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->dump(dump);
-    }
-}
-
-void InputDevice::addMapper(InputMapper* mapper) {
-    mMappers.push_back(mapper);
-}
-
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
-    mSources = 0;
-
-    if (!isIgnored()) {
-        if (!changes) { // first time only
-            mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
-        }
-
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
-            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
-                sp<KeyCharacterMap> keyboardLayout =
-                        mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
-                if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
-                    bumpGeneration();
-                }
-            }
-        }
-
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
-            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
-                std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
-                if (mAlias != alias) {
-                    mAlias = alias;
-                    bumpGeneration();
-                }
-            }
-        }
-
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
-            auto it = config->disabledDevices.find(mId);
-            bool enabled = it == config->disabledDevices.end();
-            setEnabled(enabled, when);
-        }
-
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-             // In most situations, no port will be specified.
-            mAssociatedDisplayPort = std::nullopt;
-            mAssociatedViewport = std::nullopt;
-            // Find the display port that corresponds to the current input port.
-            const std::string& inputPort = mIdentifier.location;
-            if (!inputPort.empty()) {
-                const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
-                const auto& displayPort = ports.find(inputPort);
-                if (displayPort != ports.end()) {
-                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
-                }
-            }
-
-            // If the device was explicitly disabled by the user, it would be present in the
-            // "disabledDevices" list. If it is associated with a specific display, and it was not
-            // explicitly disabled, then enable/disable the device based on whether we can find the
-            // corresponding viewport.
-            bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
-            if (mAssociatedDisplayPort) {
-                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
-                if (!mAssociatedViewport) {
-                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
-                          "but the corresponding viewport is not found.",
-                          getName().c_str(), *mAssociatedDisplayPort);
-                    enabled = false;
-                }
-            }
-
-            setEnabled(enabled, when);
-        }
-
-        for (InputMapper* mapper : mMappers) {
-            mapper->configure(when, config, changes);
-            mSources |= mapper->getSources();
-        }
-    }
-}
-
-void InputDevice::reset(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->reset(when);
-    }
-
-    mContext->updateGlobalMetaState();
-
-    notifyReset(when);
-}
-
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
-    // Process all of the events in order for each mapper.
-    // We cannot simply ask each mapper to process them in bulk because mappers may
-    // have side-effects that must be interleaved.  For example, joystick movement events and
-    // gamepad button presses are handled by different mappers but they should be dispatched
-    // in the order received.
-    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
-#if DEBUG_RAW_EVENTS
-        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
-                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
-                rawEvent->when);
-#endif
-
-        if (mDropUntilNextSync) {
-            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-                mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
-                ALOGD("Recovered from input event buffer overrun.");
-#endif
-            } else {
-#if DEBUG_RAW_EVENTS
-                ALOGD("Dropped input event while waiting for next input sync.");
-#endif
-            }
-        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
-            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
-            mDropUntilNextSync = true;
-            reset(rawEvent->when);
-        } else {
-            for (InputMapper* mapper : mMappers) {
-                mapper->process(rawEvent);
-            }
-        }
-        --count;
-    }
-}
-
-void InputDevice::timeoutExpired(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->timeoutExpired(when);
-    }
-}
-
-void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateExternalStylusState(state);
-    }
-}
-
-void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
-    outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias,
-            mIsExternal, mHasMic);
-    for (InputMapper* mapper : mMappers) {
-        mapper->populateDeviceInfo(outDeviceInfo);
-    }
-}
-
-int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
-}
-
-int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
-}
-
-int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
-    return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
-}
-
-int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
-    int32_t result = AKEY_STATE_UNKNOWN;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
-            // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
-            int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
-            if (currentResult >= AKEY_STATE_DOWN) {
-                return currentResult;
-            } else if (currentResult == AKEY_STATE_UP) {
-                result = currentResult;
-            }
-        }
-    }
-    return result;
-}
-
-bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) {
-    bool result = false;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
-        }
-    }
-    return result;
-}
-
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
-        int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->vibrate(pattern, patternSize, repeat, token);
-    }
-}
-
-void InputDevice::cancelVibrate(int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelVibrate(token);
-    }
-}
-
-void InputDevice::cancelTouch(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelTouch(when);
-    }
-}
-
-int32_t InputDevice::getMetaState() {
-    int32_t result = 0;
-    for (InputMapper* mapper : mMappers) {
-        result |= mapper->getMetaState();
-    }
-    return result;
-}
-
-void InputDevice::updateMetaState(int32_t keyCode) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateMetaState(keyCode);
-    }
-}
-
-void InputDevice::fadePointer() {
-    for (InputMapper* mapper : mMappers) {
-        mapper->fadePointer();
-    }
-}
-
-void InputDevice::bumpGeneration() {
-    mGeneration = mContext->bumpGeneration();
-}
-
-void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
-    mContext->getListener()->notifyDeviceReset(&args);
-}
-
-std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
-    // Check if we had associated to the specific display.
-    if (mAssociatedViewport) {
-        return mAssociatedViewport->displayId;
-    }
-
-    // No associated display port, check if some InputMapper is associated.
-    for (InputMapper* mapper : mMappers) {
-        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
-        if (associatedDisplayId) {
-            return associatedDisplayId;
-        }
-    }
-
-    return std::nullopt;
-}
-
-// --- CursorButtonAccumulator ---
-
-CursorButtonAccumulator::CursorButtonAccumulator() {
-    clearButtons();
-}
-
-void CursorButtonAccumulator::reset(InputDevice* device) {
-    mBtnLeft = device->isKeyPressed(BTN_LEFT);
-    mBtnRight = device->isKeyPressed(BTN_RIGHT);
-    mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
-    mBtnBack = device->isKeyPressed(BTN_BACK);
-    mBtnSide = device->isKeyPressed(BTN_SIDE);
-    mBtnForward = device->isKeyPressed(BTN_FORWARD);
-    mBtnExtra = device->isKeyPressed(BTN_EXTRA);
-    mBtnTask = device->isKeyPressed(BTN_TASK);
-}
-
-void CursorButtonAccumulator::clearButtons() {
-    mBtnLeft = 0;
-    mBtnRight = 0;
-    mBtnMiddle = 0;
-    mBtnBack = 0;
-    mBtnSide = 0;
-    mBtnForward = 0;
-    mBtnExtra = 0;
-    mBtnTask = 0;
-}
-
-void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_KEY) {
-        switch (rawEvent->code) {
-        case BTN_LEFT:
-            mBtnLeft = rawEvent->value;
-            break;
-        case BTN_RIGHT:
-            mBtnRight = rawEvent->value;
-            break;
-        case BTN_MIDDLE:
-            mBtnMiddle = rawEvent->value;
-            break;
-        case BTN_BACK:
-            mBtnBack = rawEvent->value;
-            break;
-        case BTN_SIDE:
-            mBtnSide = rawEvent->value;
-            break;
-        case BTN_FORWARD:
-            mBtnForward = rawEvent->value;
-            break;
-        case BTN_EXTRA:
-            mBtnExtra = rawEvent->value;
-            break;
-        case BTN_TASK:
-            mBtnTask = rawEvent->value;
-            break;
-        }
-    }
-}
-
-uint32_t CursorButtonAccumulator::getButtonState() const {
-    uint32_t result = 0;
-    if (mBtnLeft) {
-        result |= AMOTION_EVENT_BUTTON_PRIMARY;
-    }
-    if (mBtnRight) {
-        result |= AMOTION_EVENT_BUTTON_SECONDARY;
-    }
-    if (mBtnMiddle) {
-        result |= AMOTION_EVENT_BUTTON_TERTIARY;
-    }
-    if (mBtnBack || mBtnSide) {
-        result |= AMOTION_EVENT_BUTTON_BACK;
-    }
-    if (mBtnForward || mBtnExtra) {
-        result |= AMOTION_EVENT_BUTTON_FORWARD;
-    }
-    return result;
-}
-
-
-// --- CursorMotionAccumulator ---
-
-CursorMotionAccumulator::CursorMotionAccumulator() {
-    clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::reset(InputDevice* device) {
-    clearRelativeAxes();
-}
-
-void CursorMotionAccumulator::clearRelativeAxes() {
-    mRelX = 0;
-    mRelY = 0;
-}
-
-void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_REL) {
-        switch (rawEvent->code) {
-        case REL_X:
-            mRelX = rawEvent->value;
-            break;
-        case REL_Y:
-            mRelY = rawEvent->value;
-            break;
-        }
-    }
-}
-
-void CursorMotionAccumulator::finishSync() {
-    clearRelativeAxes();
-}
-
-
-// --- CursorScrollAccumulator ---
-
-CursorScrollAccumulator::CursorScrollAccumulator() :
-        mHaveRelWheel(false), mHaveRelHWheel(false) {
-    clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::configure(InputDevice* device) {
-    mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
-    mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
-}
-
-void CursorScrollAccumulator::reset(InputDevice* device) {
-    clearRelativeAxes();
-}
-
-void CursorScrollAccumulator::clearRelativeAxes() {
-    mRelWheel = 0;
-    mRelHWheel = 0;
-}
-
-void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_REL) {
-        switch (rawEvent->code) {
-        case REL_WHEEL:
-            mRelWheel = rawEvent->value;
-            break;
-        case REL_HWHEEL:
-            mRelHWheel = rawEvent->value;
-            break;
-        }
-    }
-}
-
-void CursorScrollAccumulator::finishSync() {
-    clearRelativeAxes();
-}
-
-
-// --- TouchButtonAccumulator ---
-
-TouchButtonAccumulator::TouchButtonAccumulator() :
-        mHaveBtnTouch(false), mHaveStylus(false) {
-    clearButtons();
-}
-
-void TouchButtonAccumulator::configure(InputDevice* device) {
-    mHaveBtnTouch = device->hasKey(BTN_TOUCH);
-    mHaveStylus = device->hasKey(BTN_TOOL_PEN)
-            || device->hasKey(BTN_TOOL_RUBBER)
-            || device->hasKey(BTN_TOOL_BRUSH)
-            || device->hasKey(BTN_TOOL_PENCIL)
-            || device->hasKey(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDevice* device) {
-    mBtnTouch = device->isKeyPressed(BTN_TOUCH);
-    mBtnStylus = device->isKeyPressed(BTN_STYLUS);
-    // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
-    mBtnStylus2 =
-            device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
-    mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
-    mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
-    mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
-    mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
-    mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
-    mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
-    mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
-    mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
-    mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
-    mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
-    mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
-    mBtnTouch = 0;
-    mBtnStylus = 0;
-    mBtnStylus2 = 0;
-    mBtnToolFinger = 0;
-    mBtnToolPen = 0;
-    mBtnToolRubber = 0;
-    mBtnToolBrush = 0;
-    mBtnToolPencil = 0;
-    mBtnToolAirbrush = 0;
-    mBtnToolMouse = 0;
-    mBtnToolLens = 0;
-    mBtnToolDoubleTap = 0;
-    mBtnToolTripleTap = 0;
-    mBtnToolQuadTap = 0;
-}
-
-void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_KEY) {
-        switch (rawEvent->code) {
-        case BTN_TOUCH:
-            mBtnTouch = rawEvent->value;
-            break;
-        case BTN_STYLUS:
-            mBtnStylus = rawEvent->value;
-            break;
-        case BTN_STYLUS2:
-        case BTN_0:// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
-            mBtnStylus2 = rawEvent->value;
-            break;
-        case BTN_TOOL_FINGER:
-            mBtnToolFinger = rawEvent->value;
-            break;
-        case BTN_TOOL_PEN:
-            mBtnToolPen = rawEvent->value;
-            break;
-        case BTN_TOOL_RUBBER:
-            mBtnToolRubber = rawEvent->value;
-            break;
-        case BTN_TOOL_BRUSH:
-            mBtnToolBrush = rawEvent->value;
-            break;
-        case BTN_TOOL_PENCIL:
-            mBtnToolPencil = rawEvent->value;
-            break;
-        case BTN_TOOL_AIRBRUSH:
-            mBtnToolAirbrush = rawEvent->value;
-            break;
-        case BTN_TOOL_MOUSE:
-            mBtnToolMouse = rawEvent->value;
-            break;
-        case BTN_TOOL_LENS:
-            mBtnToolLens = rawEvent->value;
-            break;
-        case BTN_TOOL_DOUBLETAP:
-            mBtnToolDoubleTap = rawEvent->value;
-            break;
-        case BTN_TOOL_TRIPLETAP:
-            mBtnToolTripleTap = rawEvent->value;
-            break;
-        case BTN_TOOL_QUADTAP:
-            mBtnToolQuadTap = rawEvent->value;
-            break;
-        }
-    }
-}
-
-uint32_t TouchButtonAccumulator::getButtonState() const {
-    uint32_t result = 0;
-    if (mBtnStylus) {
-        result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
-    }
-    if (mBtnStylus2) {
-        result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY;
-    }
-    return result;
-}
-
-int32_t TouchButtonAccumulator::getToolType() const {
-    if (mBtnToolMouse || mBtnToolLens) {
-        return AMOTION_EVENT_TOOL_TYPE_MOUSE;
-    }
-    if (mBtnToolRubber) {
-        return AMOTION_EVENT_TOOL_TYPE_ERASER;
-    }
-    if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
-        return AMOTION_EVENT_TOOL_TYPE_STYLUS;
-    }
-    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
-        return AMOTION_EVENT_TOOL_TYPE_FINGER;
-    }
-    return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-bool TouchButtonAccumulator::isToolActive() const {
-    return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber
-            || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush
-            || mBtnToolMouse || mBtnToolLens
-            || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
-}
-
-bool TouchButtonAccumulator::isHovering() const {
-    return mHaveBtnTouch && !mBtnTouch;
-}
-
-bool TouchButtonAccumulator::hasStylus() const {
-    return mHaveStylus;
-}
-
-
-// --- RawPointerAxes ---
-
-RawPointerAxes::RawPointerAxes() {
-    clear();
-}
-
-void RawPointerAxes::clear() {
-    x.clear();
-    y.clear();
-    pressure.clear();
-    touchMajor.clear();
-    touchMinor.clear();
-    toolMajor.clear();
-    toolMinor.clear();
-    orientation.clear();
-    distance.clear();
-    tiltX.clear();
-    tiltY.clear();
-    trackingId.clear();
-    slot.clear();
-}
-
-
-// --- RawPointerData ---
-
-RawPointerData::RawPointerData() {
-    clear();
-}
-
-void RawPointerData::clear() {
-    pointerCount = 0;
-    clearIdBits();
-}
-
-void RawPointerData::copyFrom(const RawPointerData& other) {
-    pointerCount = other.pointerCount;
-    hoveringIdBits = other.hoveringIdBits;
-    touchingIdBits = other.touchingIdBits;
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointers[i] = other.pointers[i];
-
-        int id = pointers[i].id;
-        idToIndex[id] = other.idToIndex[id];
-    }
-}
-
-void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
-    float x = 0, y = 0;
-    uint32_t count = touchingIdBits.count();
-    if (count) {
-        for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            const Pointer& pointer = pointerForId(id);
-            x += pointer.x;
-            y += pointer.y;
-        }
-        x /= count;
-        y /= count;
-    }
-    *outX = x;
-    *outY = y;
-}
-
-
-// --- CookedPointerData ---
-
-CookedPointerData::CookedPointerData() {
-    clear();
-}
-
-void CookedPointerData::clear() {
-    pointerCount = 0;
-    hoveringIdBits.clear();
-    touchingIdBits.clear();
-}
-
-void CookedPointerData::copyFrom(const CookedPointerData& other) {
-    pointerCount = other.pointerCount;
-    hoveringIdBits = other.hoveringIdBits;
-    touchingIdBits = other.touchingIdBits;
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(other.pointerProperties[i]);
-        pointerCoords[i].copyFrom(other.pointerCoords[i]);
-
-        int id = pointerProperties[i].id;
-        idToIndex[id] = other.idToIndex[id];
-    }
-}
-
-
-// --- SingleTouchMotionAccumulator ---
-
-SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
-    clearAbsoluteAxes();
-}
-
-void SingleTouchMotionAccumulator::reset(InputDevice* device) {
-    mAbsX = device->getAbsoluteAxisValue(ABS_X);
-    mAbsY = device->getAbsoluteAxisValue(ABS_Y);
-    mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
-    mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
-    mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
-    mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
-    mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
-}
-
-void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
-    mAbsX = 0;
-    mAbsY = 0;
-    mAbsPressure = 0;
-    mAbsToolWidth = 0;
-    mAbsDistance = 0;
-    mAbsTiltX = 0;
-    mAbsTiltY = 0;
-}
-
-void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_ABS) {
-        switch (rawEvent->code) {
-        case ABS_X:
-            mAbsX = rawEvent->value;
-            break;
-        case ABS_Y:
-            mAbsY = rawEvent->value;
-            break;
-        case ABS_PRESSURE:
-            mAbsPressure = rawEvent->value;
-            break;
-        case ABS_TOOL_WIDTH:
-            mAbsToolWidth = rawEvent->value;
-            break;
-        case ABS_DISTANCE:
-            mAbsDistance = rawEvent->value;
-            break;
-        case ABS_TILT_X:
-            mAbsTiltX = rawEvent->value;
-            break;
-        case ABS_TILT_Y:
-            mAbsTiltY = rawEvent->value;
-            break;
-        }
-    }
-}
-
-
-// --- MultiTouchMotionAccumulator ---
-
-MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
-      : mCurrentSlot(-1),
-        mSlots(nullptr),
-        mSlotCount(0),
-        mUsingSlotsProtocol(false),
-        mHaveStylus(false) {}
-
-MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
-    delete[] mSlots;
-}
-
-void MultiTouchMotionAccumulator::configure(InputDevice* device,
-        size_t slotCount, bool usingSlotsProtocol) {
-    mSlotCount = slotCount;
-    mUsingSlotsProtocol = usingSlotsProtocol;
-    mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
-
-    delete[] mSlots;
-    mSlots = new Slot[slotCount];
-}
-
-void MultiTouchMotionAccumulator::reset(InputDevice* device) {
-    // Unfortunately there is no way to read the initial contents of the slots.
-    // So when we reset the accumulator, we must assume they are all zeroes.
-    if (mUsingSlotsProtocol) {
-        // Query the driver for the current slot index and use it as the initial slot
-        // before we start reading events from the device.  It is possible that the
-        // current slot index will not be the same as it was when the first event was
-        // written into the evdev buffer, which means the input mapper could start
-        // out of sync with the initial state of the events in the evdev buffer.
-        // In the extremely unlikely case that this happens, the data from
-        // two slots will be confused until the next ABS_MT_SLOT event is received.
-        // This can cause the touch point to "jump", but at least there will be
-        // no stuck touches.
-        int32_t initialSlot;
-        status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(),
-                ABS_MT_SLOT, &initialSlot);
-        if (status) {
-            ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
-            initialSlot = -1;
-        }
-        clearSlots(initialSlot);
-    } else {
-        clearSlots(-1);
-    }
-}
-
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
-    if (mSlots) {
-        for (size_t i = 0; i < mSlotCount; i++) {
-            mSlots[i].clear();
-        }
-    }
-    mCurrentSlot = initialSlot;
-}
-
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_ABS) {
-        bool newSlot = false;
-        if (mUsingSlotsProtocol) {
-            if (rawEvent->code == ABS_MT_SLOT) {
-                mCurrentSlot = rawEvent->value;
-                newSlot = true;
-            }
-        } else if (mCurrentSlot < 0) {
-            mCurrentSlot = 0;
-        }
-
-        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
-            if (newSlot) {
-                ALOGW("MultiTouch device emitted invalid slot index %d but it "
-                        "should be between 0 and %zd; ignoring this slot.",
-                        mCurrentSlot, mSlotCount - 1);
-            }
-#endif
-        } else {
-            Slot* slot = &mSlots[mCurrentSlot];
-
-            switch (rawEvent->code) {
-            case ABS_MT_POSITION_X:
-                slot->mInUse = true;
-                slot->mAbsMTPositionX = rawEvent->value;
-                break;
-            case ABS_MT_POSITION_Y:
-                slot->mInUse = true;
-                slot->mAbsMTPositionY = rawEvent->value;
-                break;
-            case ABS_MT_TOUCH_MAJOR:
-                slot->mInUse = true;
-                slot->mAbsMTTouchMajor = rawEvent->value;
-                break;
-            case ABS_MT_TOUCH_MINOR:
-                slot->mInUse = true;
-                slot->mAbsMTTouchMinor = rawEvent->value;
-                slot->mHaveAbsMTTouchMinor = true;
-                break;
-            case ABS_MT_WIDTH_MAJOR:
-                slot->mInUse = true;
-                slot->mAbsMTWidthMajor = rawEvent->value;
-                break;
-            case ABS_MT_WIDTH_MINOR:
-                slot->mInUse = true;
-                slot->mAbsMTWidthMinor = rawEvent->value;
-                slot->mHaveAbsMTWidthMinor = true;
-                break;
-            case ABS_MT_ORIENTATION:
-                slot->mInUse = true;
-                slot->mAbsMTOrientation = rawEvent->value;
-                break;
-            case ABS_MT_TRACKING_ID:
-                if (mUsingSlotsProtocol && rawEvent->value < 0) {
-                    // The slot is no longer in use but it retains its previous contents,
-                    // which may be reused for subsequent touches.
-                    slot->mInUse = false;
-                } else {
-                    slot->mInUse = true;
-                    slot->mAbsMTTrackingId = rawEvent->value;
-                }
-                break;
-            case ABS_MT_PRESSURE:
-                slot->mInUse = true;
-                slot->mAbsMTPressure = rawEvent->value;
-                break;
-            case ABS_MT_DISTANCE:
-                slot->mInUse = true;
-                slot->mAbsMTDistance = rawEvent->value;
-                break;
-            case ABS_MT_TOOL_TYPE:
-                slot->mInUse = true;
-                slot->mAbsMTToolType = rawEvent->value;
-                slot->mHaveAbsMTToolType = true;
-                break;
-            }
-        }
-    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
-        // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
-        mCurrentSlot += 1;
-    }
-}
-
-void MultiTouchMotionAccumulator::finishSync() {
-    if (!mUsingSlotsProtocol) {
-        clearSlots(-1);
-    }
-}
-
-bool MultiTouchMotionAccumulator::hasStylus() const {
-    return mHaveStylus;
-}
-
-
-// --- MultiTouchMotionAccumulator::Slot ---
-
-MultiTouchMotionAccumulator::Slot::Slot() {
-    clear();
-}
-
-void MultiTouchMotionAccumulator::Slot::clear() {
-    mInUse = false;
-    mHaveAbsMTTouchMinor = false;
-    mHaveAbsMTWidthMinor = false;
-    mHaveAbsMTToolType = false;
-    mAbsMTPositionX = 0;
-    mAbsMTPositionY = 0;
-    mAbsMTTouchMajor = 0;
-    mAbsMTTouchMinor = 0;
-    mAbsMTWidthMajor = 0;
-    mAbsMTWidthMinor = 0;
-    mAbsMTOrientation = 0;
-    mAbsMTTrackingId = -1;
-    mAbsMTPressure = 0;
-    mAbsMTDistance = 0;
-    mAbsMTToolType = 0;
-}
-
-int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
-    if (mHaveAbsMTToolType) {
-        switch (mAbsMTToolType) {
-        case MT_TOOL_FINGER:
-            return AMOTION_EVENT_TOOL_TYPE_FINGER;
-        case MT_TOOL_PEN:
-            return AMOTION_EVENT_TOOL_TYPE_STYLUS;
-        }
-    }
-    return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
-
-// --- InputMapper ---
-
-InputMapper::InputMapper(InputDevice* device) :
-        mDevice(device), mContext(device->getContext()) {
-}
-
-InputMapper::~InputMapper() {
-}
-
-void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    info->addSource(getSources());
-}
-
-void InputMapper::dump(std::string& dump) {
-}
-
-void InputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-}
-
-void InputMapper::reset(nsecs_t when) {
-}
-
-void InputMapper::timeoutExpired(nsecs_t when) {
-}
-
-int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    return AKEY_STATE_UNKNOWN;
-}
-
-int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
-    return AKEY_STATE_UNKNOWN;
-}
-
-bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) {
-    return false;
-}
-
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
-        int32_t token) {
-}
-
-void InputMapper::cancelVibrate(int32_t token) {
-}
-
-void InputMapper::cancelTouch(nsecs_t when) {
-}
-
-int32_t InputMapper::getMetaState() {
-    return 0;
-}
-
-void InputMapper::updateMetaState(int32_t keyCode) {
-}
-
-void InputMapper::updateExternalStylusState(const StylusState& state) {
-
-}
-
-void InputMapper::fadePointer() {
-}
-
-status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
-    return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
-}
-
-void InputMapper::bumpGeneration() {
-    mDevice->bumpGeneration();
-}
-
-void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump,
-        const RawAbsoluteAxisInfo& axis, const char* name) {
-    if (axis.valid) {
-        dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n",
-                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution);
-    } else {
-        dump += StringPrintf(INDENT4 "%s: unknown range\n", name);
-    }
-}
-
-void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
-    dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
-    dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
-    dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
-    dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
-}
-
-// --- SwitchInputMapper ---
-
-SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
-        InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {
-}
-
-SwitchInputMapper::~SwitchInputMapper() {
-}
-
-uint32_t SwitchInputMapper::getSources() {
-    return AINPUT_SOURCE_SWITCH;
-}
-
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
-    switch (rawEvent->type) {
-    case EV_SW:
-        processSwitch(rawEvent->code, rawEvent->value);
-        break;
-
-    case EV_SYN:
-        if (rawEvent->code == SYN_REPORT) {
-            sync(rawEvent->when);
-        }
-    }
-}
-
-void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
-    if (switchCode >= 0 && switchCode < 32) {
-        if (switchValue) {
-            mSwitchValues |= 1 << switchCode;
-        } else {
-            mSwitchValues &= ~(1 << switchCode);
-        }
-        mUpdatedSwitchMask |= 1 << switchCode;
-    }
-}
-
-void SwitchInputMapper::sync(nsecs_t when) {
-    if (mUpdatedSwitchMask) {
-        uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
-                mUpdatedSwitchMask);
-        getListener()->notifySwitch(&args);
-
-        mUpdatedSwitchMask = 0;
-    }
-}
-
-int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
-    return getEventHub()->getSwitchState(getDeviceId(), switchCode);
-}
-
-void SwitchInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Switch Input Mapper:\n";
-    dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues);
-}
-
-// --- VibratorInputMapper ---
-
-VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
-        InputMapper(device), mVibrating(false) {
-}
-
-VibratorInputMapper::~VibratorInputMapper() {
-}
-
-uint32_t VibratorInputMapper::getSources() {
-    return 0;
-}
-
-void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    info->setVibrator(true);
-}
-
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
-    // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
-}
-
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
-        int32_t token) {
-#if DEBUG_VIBRATOR
-    std::string patternStr;
-    for (size_t i = 0; i < patternSize; i++) {
-        if (i != 0) {
-            patternStr += ", ";
-        }
-        patternStr += StringPrintf("%" PRId64, pattern[i]);
-    }
-    ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
-            getDeviceId(), patternStr.c_str(), repeat, token);
-#endif
-
-    mVibrating = true;
-    memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
-    mPatternSize = patternSize;
-    mRepeat = repeat;
-    mToken = token;
-    mIndex = -1;
-
-    nextStep();
-}
-
-void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
-    ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
-
-    if (mVibrating && mToken == token) {
-        stopVibrating();
-    }
-}
-
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
-    if (mVibrating) {
-        if (when >= mNextStepTime) {
-            nextStep();
-        } else {
-            getContext()->requestTimeoutAtTime(mNextStepTime);
-        }
-    }
-}
-
-void VibratorInputMapper::nextStep() {
-    mIndex += 1;
-    if (size_t(mIndex) >= mPatternSize) {
-        if (mRepeat < 0) {
-            // We are done.
-            stopVibrating();
-            return;
-        }
-        mIndex = mRepeat;
-    }
-
-    bool vibratorOn = mIndex & 1;
-    nsecs_t duration = mPattern[mIndex];
-    if (vibratorOn) {
-#if DEBUG_VIBRATOR
-        ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
-#endif
-        getEventHub()->vibrate(getDeviceId(), duration);
-    } else {
-#if DEBUG_VIBRATOR
-        ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
-        getEventHub()->cancelVibrate(getDeviceId());
-    }
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    mNextStepTime = now + duration;
-    getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
-    ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
-#endif
-}
-
-void VibratorInputMapper::stopVibrating() {
-    mVibrating = false;
-#if DEBUG_VIBRATOR
-    ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
-    getEventHub()->cancelVibrate(getDeviceId());
-}
-
-void VibratorInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Vibrator Input Mapper:\n";
-    dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
-}
-
-
-// --- KeyboardInputMapper ---
-
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
-        uint32_t source, int32_t keyboardType) :
-        InputMapper(device), mSource(source), mKeyboardType(keyboardType) {
-}
-
-KeyboardInputMapper::~KeyboardInputMapper() {
-}
-
-uint32_t KeyboardInputMapper::getSources() {
-    return mSource;
-}
-
-int32_t KeyboardInputMapper::getOrientation() {
-    if (mViewport) {
-        return mViewport->orientation;
-    }
-    return DISPLAY_ORIENTATION_0;
-}
-
-int32_t KeyboardInputMapper::getDisplayId() {
-    if (mViewport) {
-        return mViewport->displayId;
-    }
-    return ADISPLAY_ID_NONE;
-}
-
-void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    info->setKeyboardType(mKeyboardType);
-    info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
-}
-
-void KeyboardInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Keyboard Input Mapper:\n";
-    dumpParameters(dump);
-    dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
-    dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
-    dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
-    dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
-    dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
-}
-
-std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
-        nsecs_t when, const InputReaderConfiguration* config) {
-    const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
-    if (displayPort) {
-        // Find the viewport that contains the same port
-        return mDevice->getAssociatedViewport();
-    }
-
-    // No associated display defined, try to find default display if orientationAware.
-    if (mParameters.orientationAware) {
-        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-    }
-
-    return std::nullopt;
-}
-
-void KeyboardInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    if (!changes) { // first time only
-        // Configure basic parameters.
-        configureParameters();
-    }
-
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        mViewport = findViewport(when, config);
-    }
-}
-
-static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const *property) {
-    int32_t mapped = 0;
-    if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
-        for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
-            if (stemKeyRotationMap[i][0] == keyCode) {
-                stemKeyRotationMap[i][1] = mapped;
-                return;
-            }
-        }
-    }
-}
-
-void KeyboardInputMapper::configureParameters() {
-    mParameters.orientationAware = false;
-    const PropertyMap& config = getDevice()->getConfiguration();
-    config.tryGetProperty(String8("keyboard.orientationAware"),
-            mParameters.orientationAware);
-
-    if (mParameters.orientationAware) {
-        mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
-        mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
-        mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
-        mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
-    }
-
-    mParameters.handlesKeyRepeat = false;
-    config.tryGetProperty(String8("keyboard.handlesKeyRepeat"),
-            mParameters.handlesKeyRepeat);
-}
-
-void KeyboardInputMapper::dumpParameters(std::string& dump) {
-    dump += INDENT3 "Parameters:\n";
-    dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
-            toString(mParameters.orientationAware));
-    dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n",
-            toString(mParameters.handlesKeyRepeat));
-}
-
-void KeyboardInputMapper::reset(nsecs_t when) {
-    mMetaState = AMETA_NONE;
-    mDownTime = 0;
-    mKeyDowns.clear();
-    mCurrentHidUsage = 0;
-
-    resetLedState();
-
-    InputMapper::reset(when);
-}
-
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
-    switch (rawEvent->type) {
-    case EV_KEY: {
-        int32_t scanCode = rawEvent->code;
-        int32_t usageCode = mCurrentHidUsage;
-        mCurrentHidUsage = 0;
-
-        if (isKeyboardOrGamepadKey(scanCode)) {
-            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
-        }
-        break;
-    }
-    case EV_MSC: {
-        if (rawEvent->code == MSC_SCAN) {
-            mCurrentHidUsage = rawEvent->value;
-        }
-        break;
-    }
-    case EV_SYN: {
-        if (rawEvent->code == SYN_REPORT) {
-            mCurrentHidUsage = 0;
-        }
-    }
-    }
-}
-
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
-    return scanCode < BTN_MOUSE
-        || scanCode >= KEY_OK
-        || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE)
-        || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
-    switch (keyCode) {
-    case AKEYCODE_MEDIA_PLAY:
-    case AKEYCODE_MEDIA_PAUSE:
-    case AKEYCODE_MEDIA_PLAY_PAUSE:
-    case AKEYCODE_MUTE:
-    case AKEYCODE_HEADSETHOOK:
-    case AKEYCODE_MEDIA_STOP:
-    case AKEYCODE_MEDIA_NEXT:
-    case AKEYCODE_MEDIA_PREVIOUS:
-    case AKEYCODE_MEDIA_REWIND:
-    case AKEYCODE_MEDIA_RECORD:
-    case AKEYCODE_MEDIA_FAST_FORWARD:
-    case AKEYCODE_MEDIA_SKIP_FORWARD:
-    case AKEYCODE_MEDIA_SKIP_BACKWARD:
-    case AKEYCODE_MEDIA_STEP_FORWARD:
-    case AKEYCODE_MEDIA_STEP_BACKWARD:
-    case AKEYCODE_MEDIA_AUDIO_TRACK:
-    case AKEYCODE_VOLUME_UP:
-    case AKEYCODE_VOLUME_DOWN:
-    case AKEYCODE_VOLUME_MUTE:
-    case AKEYCODE_TV_AUDIO_DESCRIPTION:
-    case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
-    case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
-        return true;
-    }
-    return false;
-}
-
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
-        int32_t usageCode) {
-    int32_t keyCode;
-    int32_t keyMetaState;
-    uint32_t policyFlags;
-
-    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
-                              &keyCode, &keyMetaState, &policyFlags)) {
-        keyCode = AKEYCODE_UNKNOWN;
-        keyMetaState = mMetaState;
-        policyFlags = 0;
-    }
-
-    if (down) {
-        // Rotate key codes according to orientation if needed.
-        if (mParameters.orientationAware) {
-            keyCode = rotateKeyCode(keyCode, getOrientation());
-        }
-
-        // Add key down.
-        ssize_t keyDownIndex = findKeyDown(scanCode);
-        if (keyDownIndex >= 0) {
-            // key repeat, be sure to use same keycode as before in case of rotation
-            keyCode = mKeyDowns[keyDownIndex].keyCode;
-        } else {
-            // key down
-            if ((policyFlags & POLICY_FLAG_VIRTUAL)
-                    && mContext->shouldDropVirtualKey(when,
-                            getDevice(), keyCode, scanCode)) {
-                return;
-            }
-            if (policyFlags & POLICY_FLAG_GESTURE) {
-                mDevice->cancelTouch(when);
-            }
-
-            KeyDown keyDown;
-            keyDown.keyCode = keyCode;
-            keyDown.scanCode = scanCode;
-            mKeyDowns.push_back(keyDown);
-        }
-
-        mDownTime = when;
-    } else {
-        // Remove key down.
-        ssize_t keyDownIndex = findKeyDown(scanCode);
-        if (keyDownIndex >= 0) {
-            // key up, be sure to use same keycode as before in case of rotation
-            keyCode = mKeyDowns[keyDownIndex].keyCode;
-            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
-        } else {
-            // key was not actually down
-            ALOGI("Dropping key up from device %s because the key was not down.  "
-                    "keyCode=%d, scanCode=%d",
-                    getDeviceName().c_str(), keyCode, scanCode);
-            return;
-        }
-    }
-
-    if (updateMetaStateIfNeeded(keyCode, down)) {
-        // If global meta state changed send it along with the key.
-        // If it has not changed then we'll use what keymap gave us,
-        // since key replacement logic might temporarily reset a few
-        // meta bits for given key.
-        keyMetaState = mMetaState;
-    }
-
-    nsecs_t downTime = mDownTime;
-
-    // Key down on external an keyboard should wake the device.
-    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
-    // For internal keyboards, the key layout file should specify the policy flags for
-    // each wake key individually.
-    // TODO: Use the input device configuration to control this behavior more finely.
-    if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
-        policyFlags |= POLICY_FLAG_WAKE;
-    }
-
-    if (mParameters.handlesKeyRepeat) {
-        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
-    }
-
-    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, 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);
-}
-
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
-    size_t n = mKeyDowns.size();
-    for (size_t i = 0; i < n; i++) {
-        if (mKeyDowns[i].scanCode == scanCode) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
-}
-
-int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
-}
-
-bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) {
-    return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
-}
-
-int32_t KeyboardInputMapper::getMetaState() {
-    return mMetaState;
-}
-
-void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
-    updateMetaStateIfNeeded(keyCode, false);
-}
-
-bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
-    int32_t oldMetaState = mMetaState;
-    int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
-    bool metaStateChanged = oldMetaState != newMetaState;
-    if (metaStateChanged) {
-        mMetaState = newMetaState;
-        updateLedState(false);
-
-        getContext()->updateGlobalMetaState();
-    }
-
-    return metaStateChanged;
-}
-
-void KeyboardInputMapper::resetLedState() {
-    initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
-    initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
-    initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK);
-
-    updateLedState(true);
-}
-
-void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
-    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
-    ledState.on = false;
-}
-
-void KeyboardInputMapper::updateLedState(bool reset) {
-    updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK,
-            AMETA_CAPS_LOCK_ON, reset);
-    updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK,
-            AMETA_NUM_LOCK_ON, reset);
-    updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK,
-            AMETA_SCROLL_LOCK_ON, reset);
-}
-
-void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState,
-        int32_t led, int32_t modifier, bool reset) {
-    if (ledState.avail) {
-        bool desiredState = (mMetaState & modifier) != 0;
-        if (reset || ledState.on != desiredState) {
-            getEventHub()->setLedState(getDeviceId(), led, desiredState);
-            ledState.on = desiredState;
-        }
-    }
-}
-
-std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
-    if (mViewport) {
-        return std::make_optional(mViewport->displayId);
-    }
-    return std::nullopt;
-}
-
-// --- CursorInputMapper ---
-
-CursorInputMapper::CursorInputMapper(InputDevice* device) :
-        InputMapper(device) {
-}
-
-CursorInputMapper::~CursorInputMapper() {
-}
-
-uint32_t CursorInputMapper::getSources() {
-    return mSource;
-}
-
-void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    if (mParameters.mode == Parameters::MODE_POINTER) {
-        float minX, minY, maxX, maxY;
-        if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
-            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
-        }
-    } else {
-        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
-        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
-    }
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-
-    if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-    }
-    if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-    }
-}
-
-void CursorInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Cursor Input Mapper:\n";
-    dumpParameters(dump);
-    dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
-    dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
-    dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
-    dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
-    dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
-            toString(mCursorScrollAccumulator.haveRelativeVWheel()));
-    dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
-            toString(mCursorScrollAccumulator.haveRelativeHWheel()));
-    dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
-    dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
-    dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
-    dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
-    dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
-    dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
-}
-
-void CursorInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    if (!changes) { // first time only
-        mCursorScrollAccumulator.configure(getDevice());
-
-        // Configure basic parameters.
-        configureParameters();
-
-        // Configure device mode.
-        switch (mParameters.mode) {
-        case Parameters::MODE_POINTER_RELATIVE:
-            // Should not happen during first time configuration.
-            ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
-            mParameters.mode = Parameters::MODE_POINTER;
-            [[fallthrough]];
-        case Parameters::MODE_POINTER:
-            mSource = AINPUT_SOURCE_MOUSE;
-            mXPrecision = 1.0f;
-            mYPrecision = 1.0f;
-            mXScale = 1.0f;
-            mYScale = 1.0f;
-            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            break;
-        case Parameters::MODE_NAVIGATION:
-            mSource = AINPUT_SOURCE_TRACKBALL;
-            mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-            mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-            mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-            mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-            break;
-        }
-
-        mVWheelScale = 1.0f;
-        mHWheelScale = 1.0f;
-    }
-
-    if ((!changes && config->pointerCapture)
-            || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
-        if (config->pointerCapture) {
-            if (mParameters.mode == Parameters::MODE_POINTER) {
-                mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
-                mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
-                // Keep PointerController around in order to preserve the pointer position.
-                mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-            } else {
-                ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
-            }
-        } else {
-            if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
-                mParameters.mode = Parameters::MODE_POINTER;
-                mSource = AINPUT_SOURCE_MOUSE;
-            } else {
-                ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
-            }
-        }
-        bumpGeneration();
-        if (changes) {
-            getDevice()->notifyReset(when);
-        }
-    }
-
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
-        mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
-        mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
-        mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
-    }
-
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        mOrientation = DISPLAY_ORIENTATION_0;
-        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-            if (internalViewport) {
-                mOrientation = internalViewport->orientation;
-            }
-        }
-
-        // Update the PointerController if viewports changed.
-        if (mParameters.mode == Parameters::MODE_POINTER) {
-            getPolicy()->obtainPointerController(getDeviceId());
-        }
-        bumpGeneration();
-    }
-}
-
-void CursorInputMapper::configureParameters() {
-    mParameters.mode = Parameters::MODE_POINTER;
-    String8 cursorModeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
-        if (cursorModeString == "navigation") {
-            mParameters.mode = Parameters::MODE_NAVIGATION;
-        } else if (cursorModeString != "pointer" && cursorModeString != "default") {
-            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
-        }
-    }
-
-    mParameters.orientationAware = false;
-    getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
-            mParameters.orientationAware);
-
-    mParameters.hasAssociatedDisplay = false;
-    if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
-        mParameters.hasAssociatedDisplay = true;
-    }
-}
-
-void CursorInputMapper::dumpParameters(std::string& dump) {
-    dump += INDENT3 "Parameters:\n";
-    dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
-            toString(mParameters.hasAssociatedDisplay));
-
-    switch (mParameters.mode) {
-    case Parameters::MODE_POINTER:
-        dump += INDENT4 "Mode: pointer\n";
-        break;
-    case Parameters::MODE_POINTER_RELATIVE:
-        dump += INDENT4 "Mode: relative pointer\n";
-        break;
-    case Parameters::MODE_NAVIGATION:
-        dump += INDENT4 "Mode: navigation\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
-            toString(mParameters.orientationAware));
-}
-
-void CursorInputMapper::reset(nsecs_t when) {
-    mButtonState = 0;
-    mDownTime = 0;
-
-    mPointerVelocityControl.reset();
-    mWheelXVelocityControl.reset();
-    mWheelYVelocityControl.reset();
-
-    mCursorButtonAccumulator.reset(getDevice());
-    mCursorMotionAccumulator.reset(getDevice());
-    mCursorScrollAccumulator.reset(getDevice());
-
-    InputMapper::reset(when);
-}
-
-void CursorInputMapper::process(const RawEvent* rawEvent) {
-    mCursorButtonAccumulator.process(rawEvent);
-    mCursorMotionAccumulator.process(rawEvent);
-    mCursorScrollAccumulator.process(rawEvent);
-
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
-    }
-}
-
-void CursorInputMapper::sync(nsecs_t when) {
-    int32_t lastButtonState = mButtonState;
-    int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
-    mButtonState = currentButtonState;
-
-    bool wasDown = isPointerDown(lastButtonState);
-    bool down = isPointerDown(currentButtonState);
-    bool downChanged;
-    if (!wasDown && down) {
-        mDownTime = when;
-        downChanged = true;
-    } else if (wasDown && !down) {
-        downChanged = true;
-    } else {
-        downChanged = false;
-    }
-    nsecs_t downTime = mDownTime;
-    bool buttonsChanged = currentButtonState != lastButtonState;
-    int32_t buttonsPressed = currentButtonState & ~lastButtonState;
-    int32_t buttonsReleased = lastButtonState & ~currentButtonState;
-
-    float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
-    float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
-    bool moved = deltaX != 0 || deltaY != 0;
-
-    // Rotate delta according to orientation if needed.
-    if (mParameters.orientationAware && mParameters.hasAssociatedDisplay
-            && (deltaX != 0.0f || deltaY != 0.0f)) {
-        rotateDelta(mOrientation, &deltaX, &deltaY);
-    }
-
-    // Move the pointer.
-    PointerProperties pointerProperties;
-    pointerProperties.clear();
-    pointerProperties.id = 0;
-    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
-
-    PointerCoords pointerCoords;
-    pointerCoords.clear();
-
-    float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
-    float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
-    bool scrolled = vscroll != 0 || hscroll != 0;
-
-    mWheelYVelocityControl.move(when, nullptr, &vscroll);
-    mWheelXVelocityControl.move(when, &hscroll, nullptr);
-
-    mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-    int32_t displayId;
-    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-    if (mSource == AINPUT_SOURCE_MOUSE) {
-        if (moved || scrolled || buttonsChanged) {
-            mPointerController->setPresentation(
-                    PointerControllerInterface::PRESENTATION_POINTER);
-
-            if (moved) {
-                mPointerController->move(deltaX, deltaY);
-            }
-
-            if (buttonsChanged) {
-                mPointerController->setButtonState(currentButtonState);
-            }
-
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-        }
-
-        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = mPointerController->getDisplayId();
-    } else {
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
-        displayId = ADISPLAY_ID_NONE;
-    }
-
-    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
-
-    // Moving an external trackball or mouse should wake the device.
-    // We don't do this for internal cursor devices to prevent them from waking up
-    // the device in your pocket.
-    // TODO: Use the input device configuration to control this behavior more finely.
-    uint32_t policyFlags = 0;
-    if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
-        policyFlags |= POLICY_FLAG_WAKE;
-    }
-
-    // Synthesize key down from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-            displayId, policyFlags, lastButtonState, currentButtonState);
-
-    // Send motion event.
-    if (downChanged || moved || scrolled || buttonsChanged) {
-        int32_t metaState = mContext->getGlobalMetaState();
-        int32_t buttonState = lastButtonState;
-        int32_t motionEventAction;
-        if (downChanged) {
-            motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
-        } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
-            motionEventAction = AMOTION_EVENT_ACTION_MOVE;
-        } else {
-            motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
-        }
-
-        if (buttonsReleased) {
-            BitSet32 released(buttonsReleased);
-            while (!released.isEmpty()) {
-                int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
-                buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
-                                             mSource, displayId, policyFlags,
-                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                             metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision,
-                                             xCursorPosition, yCursorPosition, downTime,
-                                             /* videoFrames */ {});
-                getListener()->notifyMotion(&releaseArgs);
-            }
-        }
-
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), 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,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-
-        if (buttonsPressed) {
-            BitSet32 pressed(buttonsPressed);
-            while (!pressed.isEmpty()) {
-                int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
-                buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
-                                           mSource, displayId, policyFlags,
-                                           AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
-                                           metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision,
-                                           xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {});
-                getListener()->notifyMotion(&pressArgs);
-            }
-        }
-
-        ALOG_ASSERT(buttonState == currentButtonState);
-
-        // 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(mContext->getNextSequenceNum(), when, 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 */ {});
-            getListener()->notifyMotion(&hoverArgs);
-        }
-
-        // Send scroll events.
-        if (scrolled) {
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
-            NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, 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 */ {});
-            getListener()->notifyMotion(&scrollArgs);
-        }
-    }
-
-    // Synthesize key up from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
-            displayId, policyFlags, lastButtonState, currentButtonState);
-
-    mCursorMotionAccumulator.finishSync();
-    mCursorScrollAccumulator.finishSync();
-}
-
-int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
-        return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
-    } else {
-        return AKEY_STATE_UNKNOWN;
-    }
-}
-
-void CursorInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-    }
-}
-
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
-    if (mParameters.hasAssociatedDisplay) {
-        if (mParameters.mode == Parameters::MODE_POINTER) {
-            return std::make_optional(mPointerController->getDisplayId());
-        } else {
-            // If the device is orientationAware and not a mouse,
-            // it expects to dispatch events to any display
-            return std::make_optional(ADISPLAY_ID_NONE);
-        }
-    }
-    return std::nullopt;
-}
-
-// --- RotaryEncoderInputMapper ---
-
-RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
-        InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
-    mSource = AINPUT_SOURCE_ROTARY_ENCODER;
-}
-
-RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {
-}
-
-uint32_t RotaryEncoderInputMapper::getSources() {
-    return mSource;
-}
-
-void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
-        float res = 0.0f;
-        if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
-            ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
-        }
-        if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
-            mScalingFactor)) {
-            ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
-              "default to 1.0!\n");
-            mScalingFactor = 1.0f;
-        }
-        info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-            res * mScalingFactor);
-    }
-}
-
-void RotaryEncoderInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Rotary Encoder Input Mapper:\n";
-    dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
-            toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
-}
-
-void RotaryEncoderInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-    if (!changes) {
-        mRotaryEncoderScrollAccumulator.configure(getDevice());
-    }
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        std::optional<DisplayViewport> internalViewport =
-                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-        if (internalViewport) {
-            mOrientation = internalViewport->orientation;
-        } else {
-            mOrientation = DISPLAY_ORIENTATION_0;
-        }
-    }
-}
-
-void RotaryEncoderInputMapper::reset(nsecs_t when) {
-    mRotaryEncoderScrollAccumulator.reset(getDevice());
-
-    InputMapper::reset(when);
-}
-
-void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
-    mRotaryEncoderScrollAccumulator.process(rawEvent);
-
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
-    }
-}
-
-void RotaryEncoderInputMapper::sync(nsecs_t when) {
-    PointerCoords pointerCoords;
-    pointerCoords.clear();
-
-    PointerProperties pointerProperties;
-    pointerProperties.clear();
-    pointerProperties.id = 0;
-    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-
-    float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
-    bool scrolled = scroll != 0;
-
-    // This is not a pointer, so it's not associated with a display.
-    int32_t displayId = ADISPLAY_ID_NONE;
-
-    // Moving the rotary encoder should wake the device (if specified).
-    uint32_t policyFlags = 0;
-    if (scrolled && getDevice()->isExternal()) {
-        policyFlags |= POLICY_FLAG_WAKE;
-    }
-
-    if (mOrientation == DISPLAY_ORIENTATION_180) {
-        scroll = -scroll;
-    }
-
-    // Send motion event.
-    if (scrolled) {
-        int32_t metaState = mContext->getGlobalMetaState();
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
-
-        NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, 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 */ {});
-        getListener()->notifyMotion(&scrollArgs);
-    }
-
-    mRotaryEncoderScrollAccumulator.finishSync();
-}
-
-// --- TouchInputMapper ---
-
-TouchInputMapper::TouchInputMapper(InputDevice* device) :
-        InputMapper(device),
-        mSource(0), mDeviceMode(DEVICE_MODE_DISABLED),
-        mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0),
-        mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0),
-        mSurfaceOrientation(DISPLAY_ORIENTATION_0) {
-}
-
-TouchInputMapper::~TouchInputMapper() {
-}
-
-uint32_t TouchInputMapper::getSources() {
-    return mSource;
-}
-
-void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    if (mDeviceMode != DEVICE_MODE_DISABLED) {
-        info->addMotionRange(mOrientedRanges.x);
-        info->addMotionRange(mOrientedRanges.y);
-        info->addMotionRange(mOrientedRanges.pressure);
-
-        if (mOrientedRanges.haveSize) {
-            info->addMotionRange(mOrientedRanges.size);
-        }
-
-        if (mOrientedRanges.haveTouchSize) {
-            info->addMotionRange(mOrientedRanges.touchMajor);
-            info->addMotionRange(mOrientedRanges.touchMinor);
-        }
-
-        if (mOrientedRanges.haveToolSize) {
-            info->addMotionRange(mOrientedRanges.toolMajor);
-            info->addMotionRange(mOrientedRanges.toolMinor);
-        }
-
-        if (mOrientedRanges.haveOrientation) {
-            info->addMotionRange(mOrientedRanges.orientation);
-        }
-
-        if (mOrientedRanges.haveDistance) {
-            info->addMotionRange(mOrientedRanges.distance);
-        }
-
-        if (mOrientedRanges.haveTilt) {
-            info->addMotionRange(mOrientedRanges.tilt);
-        }
-
-        if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                    0.0f);
-        }
-        if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                    0.0f);
-        }
-        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
-            const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
-            const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
-                    x.fuzz, x.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
-                    y.fuzz, y.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
-                    x.fuzz, x.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
-                    y.fuzz, y.resolution);
-        }
-        info->setButtonUnderPad(mParameters.hasButtonUnderPad);
-    }
-}
-
-void TouchInputMapper::dump(std::string& dump) {
-    dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
-    dumpParameters(dump);
-    dumpVirtualKeys(dump);
-    dumpRawPointerAxes(dump);
-    dumpCalibration(dump);
-    dumpAffineTransformation(dump);
-    dumpSurface(dump);
-
-    dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
-    dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
-    dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
-    dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
-    dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
-    dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
-    dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
-    dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
-    dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
-    dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
-    dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
-    dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
-    dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt));
-    dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter);
-    dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale);
-    dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
-    dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
-
-    dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
-    dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
-            mLastRawState.rawPointerData.pointerCount);
-    for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
-        const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
-        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
-                "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
-                "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
-                "toolType=%d, isHovering=%s\n", i,
-                pointer.id, pointer.x, pointer.y, pointer.pressure,
-                pointer.touchMajor, pointer.touchMinor,
-                pointer.toolMajor, pointer.toolMinor,
-                pointer.orientation, pointer.tiltX, pointer.tiltY, pointer.distance,
-                pointer.toolType, toString(pointer.isHovering));
-    }
-
-    dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", mLastCookedState.buttonState);
-    dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
-            mLastCookedState.cookedPointerData.pointerCount);
-    for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) {
-        const PointerProperties& pointerProperties =
-                mLastCookedState.cookedPointerData.pointerProperties[i];
-        const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
-        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
-                "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, "
-                "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
-                "toolType=%d, isHovering=%s\n", i,
-                pointerProperties.id,
-                pointerCoords.getX(),
-                pointerCoords.getY(),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
-                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
-                pointerProperties.toolType,
-                toString(mLastCookedState.cookedPointerData.isHovering(i)));
-    }
-
-    dump += INDENT3 "Stylus Fusion:\n";
-    dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
-            toString(mExternalStylusConnected));
-    dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
-    dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
-            mExternalStylusFusionTimeout);
-    dump += INDENT3 "External Stylus State:\n";
-    dumpStylusState(dump, mExternalStylusState);
-
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
-        dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
-        dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n",
-                mPointerXMovementScale);
-        dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n",
-                mPointerYMovementScale);
-        dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n",
-                mPointerXZoomScale);
-        dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n",
-                mPointerYZoomScale);
-        dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n",
-                mPointerGestureMaxSwipeWidth);
-    }
-}
-
-const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
-    switch (deviceMode) {
-    case DEVICE_MODE_DISABLED:
-        return "disabled";
-    case DEVICE_MODE_DIRECT:
-        return "direct";
-    case DEVICE_MODE_UNSCALED:
-        return "unscaled";
-    case DEVICE_MODE_NAVIGATION:
-        return "navigation";
-    case DEVICE_MODE_POINTER:
-        return "pointer";
-    }
-    return "unknown";
-}
-
-void TouchInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    mConfig = *config;
-
-    if (!changes) { // first time only
-        // Configure basic parameters.
-        configureParameters();
-
-        // Configure common accumulators.
-        mCursorScrollAccumulator.configure(getDevice());
-        mTouchButtonAccumulator.configure(getDevice());
-
-        // Configure absolute axis information.
-        configureRawPointerAxes();
-
-        // Prepare input device calibration.
-        parseCalibration();
-        resolveCalibration();
-    }
-
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
-        // Update location calibration to reflect current settings
-        updateAffineTransformation();
-    }
-
-    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
-        // Update pointer speed.
-        mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
-        mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
-        mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
-    }
-
-    bool resetNeeded = false;
-    if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
-            | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
-            | InputReaderConfiguration::CHANGE_SHOW_TOUCHES
-            | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
-        // Configure device sources, surface dimensions, orientation and
-        // scaling factors.
-        configureSurface(when, &resetNeeded);
-    }
-
-    if (changes && resetNeeded) {
-        // Send reset, unless this is the first time the device has been configured,
-        // in which case the reader will call reset itself after all mappers are ready.
-        getDevice()->notifyReset(when);
-    }
-}
-
-void TouchInputMapper::resolveExternalStylusPresence() {
-    std::vector<InputDeviceInfo> devices;
-    mContext->getExternalStylusDevices(devices);
-    mExternalStylusConnected = !devices.empty();
-
-    if (!mExternalStylusConnected) {
-        resetExternalStylus();
-    }
-}
-
-void TouchInputMapper::configureParameters() {
-    // Use the pointer presentation mode for devices that do not support distinct
-    // multitouch.  The spot-based presentation relies on being able to accurately
-    // locate two or more fingers on the touch pad.
-    mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
-            ? Parameters::GESTURE_MODE_SINGLE_TOUCH : Parameters::GESTURE_MODE_MULTI_TOUCH;
-
-    String8 gestureModeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
-            gestureModeString)) {
-        if (gestureModeString == "single-touch") {
-            mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
-        } else if (gestureModeString == "multi-touch") {
-            mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
-        } else if (gestureModeString != "default") {
-            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
-        }
-    }
-
-    if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
-        // The device is a touch screen.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
-        // The device is a pointing device like a track pad.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
-    } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
-            || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
-        // The device is a cursor device with a touch pad attached.
-        // By default don't use the touch pad to move the pointer.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
-    } else {
-        // The device is a touch pad of unknown purpose.
-        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
-    }
-
-    mParameters.hasButtonUnderPad=
-            getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
-
-    String8 deviceTypeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
-            deviceTypeString)) {
-        if (deviceTypeString == "touchScreen") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-        } else if (deviceTypeString == "touchPad") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
-        } else if (deviceTypeString == "touchNavigation") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
-        } else if (deviceTypeString == "pointer") {
-            mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
-        } else if (deviceTypeString != "default") {
-            ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
-        }
-    }
-
-    mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-    getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
-            mParameters.orientationAware);
-
-    mParameters.hasAssociatedDisplay = false;
-    mParameters.associatedDisplayIsExternal = false;
-    if (mParameters.orientationAware
-            || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
-            || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
-        mParameters.hasAssociatedDisplay = true;
-        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
-            mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
-            String8 uniqueDisplayId;
-            getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
-                    uniqueDisplayId);
-            mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
-        }
-    }
-    if (getDevice()->getAssociatedDisplayPort()) {
-        mParameters.hasAssociatedDisplay = true;
-    }
-
-    // Initial downs on external touch devices should wake the device.
-    // Normally we don't do this for internal touch screens to prevent them from waking
-    // up in your pocket but you can enable it using the input device configuration.
-    mParameters.wake = getDevice()->isExternal();
-    getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"),
-            mParameters.wake);
-}
-
-void TouchInputMapper::dumpParameters(std::string& dump) {
-    dump += INDENT3 "Parameters:\n";
-
-    switch (mParameters.gestureMode) {
-    case Parameters::GESTURE_MODE_SINGLE_TOUCH:
-        dump += INDENT4 "GestureMode: single-touch\n";
-        break;
-    case Parameters::GESTURE_MODE_MULTI_TOUCH:
-        dump += INDENT4 "GestureMode: multi-touch\n";
-        break;
-    default:
-        assert(false);
-    }
-
-    switch (mParameters.deviceType) {
-    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
-        dump += INDENT4 "DeviceType: touchScreen\n";
-        break;
-    case Parameters::DEVICE_TYPE_TOUCH_PAD:
-        dump += INDENT4 "DeviceType: touchPad\n";
-        break;
-    case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
-        dump += INDENT4 "DeviceType: touchNavigation\n";
-        break;
-    case Parameters::DEVICE_TYPE_POINTER:
-        dump += INDENT4 "DeviceType: pointer\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    dump += StringPrintf(
-            INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n",
-            toString(mParameters.hasAssociatedDisplay),
-            toString(mParameters.associatedDisplayIsExternal),
-            mParameters.uniqueDisplayId.c_str());
-    dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
-            toString(mParameters.orientationAware));
-}
-
-void TouchInputMapper::configureRawPointerAxes() {
-    mRawPointerAxes.clear();
-}
-
-void TouchInputMapper::dumpRawPointerAxes(std::string& dump) {
-    dump += INDENT3 "Raw Touch Axes:\n";
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
-    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
-}
-
-bool TouchInputMapper::hasExternalStylus() const {
-    return mExternalStylusConnected;
-}
-
-/**
- * Determine which DisplayViewport to use.
- * 1. If display port is specified, return the matching viewport. If matching viewport not
- * found, then return.
- * 2. If a device has associated display, get the matching viewport by either unique id or by
- * the display type (internal or external).
- * 3. Otherwise, use a non-display viewport.
- */
-std::optional<DisplayViewport> TouchInputMapper::findViewport() {
-    if (mParameters.hasAssociatedDisplay) {
-        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
-        if (displayPort) {
-            // Find the viewport that contains the same port
-            return mDevice->getAssociatedViewport();
-        }
-
-        // Check if uniqueDisplayId is specified in idc file.
-        if (!mParameters.uniqueDisplayId.empty()) {
-            return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
-        }
-
-        ViewportType viewportTypeToUse;
-        if (mParameters.associatedDisplayIsExternal) {
-            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
-        } else {
-            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
-        }
-
-        std::optional<DisplayViewport> viewport =
-                mConfig.getDisplayViewportByType(viewportTypeToUse);
-        if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
-            ALOGW("Input device %s should be associated with external display, "
-                    "fallback to internal one for the external viewport is not found.",
-                        getDeviceName().c_str());
-            viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-        }
-
-        return viewport;
-    }
-
-    // No associated display, return a non-display viewport.
-    DisplayViewport newViewport;
-    // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.getRawWidth();
-    int32_t rawHeight = mRawPointerAxes.getRawHeight();
-    newViewport.setNonDisplayViewport(rawWidth, rawHeight);
-    return std::make_optional(newViewport);
-}
-
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
-    int32_t oldDeviceMode = mDeviceMode;
-
-    resolveExternalStylusPresence();
-
-    // Determine device mode.
-    if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
-            && mConfig.pointerGesturesEnabled) {
-        mSource = AINPUT_SOURCE_MOUSE;
-        mDeviceMode = DEVICE_MODE_POINTER;
-        if (hasStylus()) {
-            mSource |= AINPUT_SOURCE_STYLUS;
-        }
-    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
-            && mParameters.hasAssociatedDisplay) {
-        mSource = AINPUT_SOURCE_TOUCHSCREEN;
-        mDeviceMode = DEVICE_MODE_DIRECT;
-        if (hasStylus()) {
-            mSource |= AINPUT_SOURCE_STYLUS;
-        }
-        if (hasExternalStylus()) {
-            mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
-        }
-    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
-        mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
-        mDeviceMode = DEVICE_MODE_NAVIGATION;
-    } else {
-        mSource = AINPUT_SOURCE_TOUCHPAD;
-        mDeviceMode = DEVICE_MODE_UNSCALED;
-    }
-
-    // Ensure we have valid X and Y axes.
-    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
-        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
-                "The device will be inoperable.", getDeviceName().c_str());
-        mDeviceMode = DEVICE_MODE_DISABLED;
-        return;
-    }
-
-    // Get associated display dimensions.
-    std::optional<DisplayViewport> newViewport = findViewport();
-    if (!newViewport) {
-        ALOGI("Touch device '%s' could not query the properties of its associated "
-                "display.  The device will be inoperable until the display size "
-                "becomes available.",
-                getDeviceName().c_str());
-        mDeviceMode = DEVICE_MODE_DISABLED;
-        return;
-    }
-
-    // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.getRawWidth();
-    int32_t rawHeight = mRawPointerAxes.getRawHeight();
-
-    bool viewportChanged = mViewport != *newViewport;
-    if (viewportChanged) {
-        mViewport = *newViewport;
-
-        if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
-            // Convert rotated viewport to natural surface coordinates.
-            int32_t naturalLogicalWidth, naturalLogicalHeight;
-            int32_t naturalPhysicalWidth, naturalPhysicalHeight;
-            int32_t naturalPhysicalLeft, naturalPhysicalTop;
-            int32_t naturalDeviceWidth, naturalDeviceHeight;
-            switch (mViewport.orientation) {
-            case DISPLAY_ORIENTATION_90:
-                naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
-                naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
-                naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
-                naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
-                naturalPhysicalTop = mViewport.physicalLeft;
-                naturalDeviceWidth = mViewport.deviceHeight;
-                naturalDeviceHeight = mViewport.deviceWidth;
-                break;
-            case DISPLAY_ORIENTATION_180:
-                naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
-                naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
-                naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
-                naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
-                naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
-                naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
-                naturalDeviceWidth = mViewport.deviceWidth;
-                naturalDeviceHeight = mViewport.deviceHeight;
-                break;
-            case DISPLAY_ORIENTATION_270:
-                naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
-                naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
-                naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
-                naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                naturalPhysicalLeft = mViewport.physicalTop;
-                naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
-                naturalDeviceWidth = mViewport.deviceHeight;
-                naturalDeviceHeight = mViewport.deviceWidth;
-                break;
-            case DISPLAY_ORIENTATION_0:
-            default:
-                naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
-                naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
-                naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
-                naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
-                naturalPhysicalLeft = mViewport.physicalLeft;
-                naturalPhysicalTop = mViewport.physicalTop;
-                naturalDeviceWidth = mViewport.deviceWidth;
-                naturalDeviceHeight = mViewport.deviceHeight;
-                break;
-            }
-
-            if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
-                ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
-                naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
-                naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
-            }
-
-            mPhysicalWidth = naturalPhysicalWidth;
-            mPhysicalHeight = naturalPhysicalHeight;
-            mPhysicalLeft = naturalPhysicalLeft;
-            mPhysicalTop = naturalPhysicalTop;
-
-            mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
-            mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
-            mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
-            mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
-
-            mSurfaceOrientation = mParameters.orientationAware ?
-                    mViewport.orientation : DISPLAY_ORIENTATION_0;
-        } else {
-            mPhysicalWidth = rawWidth;
-            mPhysicalHeight = rawHeight;
-            mPhysicalLeft = 0;
-            mPhysicalTop = 0;
-
-            mSurfaceWidth = rawWidth;
-            mSurfaceHeight = rawHeight;
-            mSurfaceLeft = 0;
-            mSurfaceTop = 0;
-            mSurfaceOrientation = DISPLAY_ORIENTATION_0;
-        }
-    }
-
-    // If moving between pointer modes, need to reset some state.
-    bool deviceModeChanged = mDeviceMode != oldDeviceMode;
-    if (deviceModeChanged) {
-        mOrientedRanges.clear();
-    }
-
-    // Create or update pointer controller if needed.
-    if (mDeviceMode == DEVICE_MODE_POINTER ||
-            (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
-        if (mPointerController == nullptr || viewportChanged) {
-            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-        }
-    } else {
-        mPointerController.clear();
-    }
-
-    if (viewportChanged || deviceModeChanged) {
-        ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
-                "display id %d",
-                getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
-                mSurfaceOrientation, mDeviceMode, mViewport.displayId);
-
-        // Configure X and Y factors.
-        mXScale = float(mSurfaceWidth) / rawWidth;
-        mYScale = float(mSurfaceHeight) / rawHeight;
-        mXTranslate = -mSurfaceLeft;
-        mYTranslate = -mSurfaceTop;
-        mXPrecision = 1.0f / mXScale;
-        mYPrecision = 1.0f / mYScale;
-
-        mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
-        mOrientedRanges.x.source = mSource;
-        mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
-        mOrientedRanges.y.source = mSource;
-
-        configureVirtualKeys();
-
-        // Scale factor for terms that are not oriented in a particular axis.
-        // If the pixels are square then xScale == yScale otherwise we fake it
-        // by choosing an average.
-        mGeometricScale = avg(mXScale, mYScale);
-
-        // Size of diagonal axis.
-        float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
-
-        // Size factors.
-        if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
-            if (mRawPointerAxes.touchMajor.valid
-                    && mRawPointerAxes.touchMajor.maxValue != 0) {
-                mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
-            } else if (mRawPointerAxes.toolMajor.valid
-                    && mRawPointerAxes.toolMajor.maxValue != 0) {
-                mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
-            } else {
-                mSizeScale = 0.0f;
-            }
-
-            mOrientedRanges.haveTouchSize = true;
-            mOrientedRanges.haveToolSize = true;
-            mOrientedRanges.haveSize = true;
-
-            mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
-            mOrientedRanges.touchMajor.source = mSource;
-            mOrientedRanges.touchMajor.min = 0;
-            mOrientedRanges.touchMajor.max = diagonalSize;
-            mOrientedRanges.touchMajor.flat = 0;
-            mOrientedRanges.touchMajor.fuzz = 0;
-            mOrientedRanges.touchMajor.resolution = 0;
-
-            mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
-            mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
-
-            mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
-            mOrientedRanges.toolMajor.source = mSource;
-            mOrientedRanges.toolMajor.min = 0;
-            mOrientedRanges.toolMajor.max = diagonalSize;
-            mOrientedRanges.toolMajor.flat = 0;
-            mOrientedRanges.toolMajor.fuzz = 0;
-            mOrientedRanges.toolMajor.resolution = 0;
-
-            mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
-            mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
-
-            mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
-            mOrientedRanges.size.source = mSource;
-            mOrientedRanges.size.min = 0;
-            mOrientedRanges.size.max = 1.0;
-            mOrientedRanges.size.flat = 0;
-            mOrientedRanges.size.fuzz = 0;
-            mOrientedRanges.size.resolution = 0;
-        } else {
-            mSizeScale = 0.0f;
-        }
-
-        // Pressure factors.
-        mPressureScale = 0;
-        float pressureMax = 1.0;
-        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
-                || mCalibration.pressureCalibration
-                        == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
-            if (mCalibration.havePressureScale) {
-                mPressureScale = mCalibration.pressureScale;
-                pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
-            } else if (mRawPointerAxes.pressure.valid
-                    && mRawPointerAxes.pressure.maxValue != 0) {
-                mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
-            }
-        }
-
-        mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
-        mOrientedRanges.pressure.source = mSource;
-        mOrientedRanges.pressure.min = 0;
-        mOrientedRanges.pressure.max = pressureMax;
-        mOrientedRanges.pressure.flat = 0;
-        mOrientedRanges.pressure.fuzz = 0;
-        mOrientedRanges.pressure.resolution = 0;
-
-        // Tilt
-        mTiltXCenter = 0;
-        mTiltXScale = 0;
-        mTiltYCenter = 0;
-        mTiltYScale = 0;
-        mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
-        if (mHaveTilt) {
-            mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue,
-                    mRawPointerAxes.tiltX.maxValue);
-            mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue,
-                    mRawPointerAxes.tiltY.maxValue);
-            mTiltXScale = M_PI / 180;
-            mTiltYScale = M_PI / 180;
-
-            mOrientedRanges.haveTilt = true;
-
-            mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
-            mOrientedRanges.tilt.source = mSource;
-            mOrientedRanges.tilt.min = 0;
-            mOrientedRanges.tilt.max = M_PI_2;
-            mOrientedRanges.tilt.flat = 0;
-            mOrientedRanges.tilt.fuzz = 0;
-            mOrientedRanges.tilt.resolution = 0;
-        }
-
-        // Orientation
-        mOrientationScale = 0;
-        if (mHaveTilt) {
-            mOrientedRanges.haveOrientation = true;
-
-            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
-            mOrientedRanges.orientation.source = mSource;
-            mOrientedRanges.orientation.min = -M_PI;
-            mOrientedRanges.orientation.max = M_PI;
-            mOrientedRanges.orientation.flat = 0;
-            mOrientedRanges.orientation.fuzz = 0;
-            mOrientedRanges.orientation.resolution = 0;
-        } else if (mCalibration.orientationCalibration !=
-                Calibration::ORIENTATION_CALIBRATION_NONE) {
-            if (mCalibration.orientationCalibration
-                    == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
-                if (mRawPointerAxes.orientation.valid) {
-                    if (mRawPointerAxes.orientation.maxValue > 0) {
-                        mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
-                    } else if (mRawPointerAxes.orientation.minValue < 0) {
-                        mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
-                    } else {
-                        mOrientationScale = 0;
-                    }
-                }
-            }
-
-            mOrientedRanges.haveOrientation = true;
-
-            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
-            mOrientedRanges.orientation.source = mSource;
-            mOrientedRanges.orientation.min = -M_PI_2;
-            mOrientedRanges.orientation.max = M_PI_2;
-            mOrientedRanges.orientation.flat = 0;
-            mOrientedRanges.orientation.fuzz = 0;
-            mOrientedRanges.orientation.resolution = 0;
-        }
-
-        // Distance
-        mDistanceScale = 0;
-        if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
-            if (mCalibration.distanceCalibration
-                    == Calibration::DISTANCE_CALIBRATION_SCALED) {
-                if (mCalibration.haveDistanceScale) {
-                    mDistanceScale = mCalibration.distanceScale;
-                } else {
-                    mDistanceScale = 1.0f;
-                }
-            }
-
-            mOrientedRanges.haveDistance = true;
-
-            mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
-            mOrientedRanges.distance.source = mSource;
-            mOrientedRanges.distance.min =
-                    mRawPointerAxes.distance.minValue * mDistanceScale;
-            mOrientedRanges.distance.max =
-                    mRawPointerAxes.distance.maxValue * mDistanceScale;
-            mOrientedRanges.distance.flat = 0;
-            mOrientedRanges.distance.fuzz =
-                    mRawPointerAxes.distance.fuzz * mDistanceScale;
-            mOrientedRanges.distance.resolution = 0;
-        }
-
-        // Compute oriented precision, scales and ranges.
-        // Note that the maximum value reported is an inclusive maximum value so it is one
-        // unit less than the total width or height of surface.
-        switch (mSurfaceOrientation) {
-        case DISPLAY_ORIENTATION_90:
-        case DISPLAY_ORIENTATION_270:
-            mOrientedXPrecision = mYPrecision;
-            mOrientedYPrecision = mXPrecision;
-
-            mOrientedRanges.x.min = mYTranslate;
-            mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
-            mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = 0;
-            mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
-
-            mOrientedRanges.y.min = mXTranslate;
-            mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
-            mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = 0;
-            mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
-            break;
-
-        default:
-            mOrientedXPrecision = mXPrecision;
-            mOrientedYPrecision = mYPrecision;
-
-            mOrientedRanges.x.min = mXTranslate;
-            mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
-            mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = 0;
-            mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
-
-            mOrientedRanges.y.min = mYTranslate;
-            mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
-            mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = 0;
-            mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
-            break;
-        }
-
-        // Location
-        updateAffineTransformation();
-
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
-            // Compute pointer gesture detection parameters.
-            float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
-
-            // Scale movements such that one whole swipe of the touch pad covers a
-            // given area relative to the diagonal size of the display when no acceleration
-            // is applied.
-            // Assume that the touch pad has a square aspect ratio such that movements in
-            // X and Y of the same number of raw units cover the same physical distance.
-            mPointerXMovementScale = mConfig.pointerGestureMovementSpeedRatio
-                    * displayDiagonal / rawDiagonal;
-            mPointerYMovementScale = mPointerXMovementScale;
-
-            // Scale zooms to cover a smaller range of the display than movements do.
-            // This value determines the area around the pointer that is affected by freeform
-            // pointer gestures.
-            mPointerXZoomScale = mConfig.pointerGestureZoomSpeedRatio
-                    * displayDiagonal / rawDiagonal;
-            mPointerYZoomScale = mPointerXZoomScale;
-
-            // Max width between pointers to detect a swipe gesture is more than some fraction
-            // of the diagonal axis of the touch pad.  Touches that are wider than this are
-            // translated into freeform gestures.
-            mPointerGestureMaxSwipeWidth =
-                    mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
-
-            // Abort current pointer usages because the state has changed.
-            abortPointerUsage(when, 0 /*policyFlags*/);
-        }
-
-        // Inform the dispatcher about the changes.
-        *outResetNeeded = true;
-        bumpGeneration();
-    }
-}
-
-void TouchInputMapper::dumpSurface(std::string& dump) {
-    dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
-    dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
-    dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
-    dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
-    dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
-    dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
-    dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
-    dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
-    dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
-    dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
-}
-
-void TouchInputMapper::configureVirtualKeys() {
-    std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
-    getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
-
-    mVirtualKeys.clear();
-
-    if (virtualKeyDefinitions.size() == 0) {
-        return;
-    }
-
-    int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
-    int32_t touchScreenTop = mRawPointerAxes.y.minValue;
-    int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
-    int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
-
-    for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) {
-        VirtualKey virtualKey;
-
-        virtualKey.scanCode = virtualKeyDefinition.scanCode;
-        int32_t keyCode;
-        int32_t dummyKeyMetaState;
-        uint32_t flags;
-        if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0,
-                                  &keyCode, &dummyKeyMetaState, &flags)) {
-            ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
-                    virtualKey.scanCode);
-            continue; // drop the key
-        }
-
-        virtualKey.keyCode = keyCode;
-        virtualKey.flags = flags;
-
-        // convert the key definition's display coordinates into touch coordinates for a hit box
-        int32_t halfWidth = virtualKeyDefinition.width / 2;
-        int32_t halfHeight = virtualKeyDefinition.height / 2;
-
-        virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
-                * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
-        virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
-                * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
-        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
-                * touchScreenHeight / mSurfaceHeight + touchScreenTop;
-        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
-                * touchScreenHeight / mSurfaceHeight + touchScreenTop;
-        mVirtualKeys.push_back(virtualKey);
-    }
-}
-
-void TouchInputMapper::dumpVirtualKeys(std::string& dump) {
-    if (!mVirtualKeys.empty()) {
-        dump += INDENT3 "Virtual Keys:\n";
-
-        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
-            const VirtualKey& virtualKey = mVirtualKeys[i];
-            dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
-                    "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
-                    i, virtualKey.scanCode, virtualKey.keyCode,
-                    virtualKey.hitLeft, virtualKey.hitRight,
-                    virtualKey.hitTop, virtualKey.hitBottom);
-        }
-    }
-}
-
-void TouchInputMapper::parseCalibration() {
-    const PropertyMap& in = getDevice()->getConfiguration();
-    Calibration& out = mCalibration;
-
-    // Size
-    out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
-    String8 sizeCalibrationString;
-    if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
-        if (sizeCalibrationString == "none") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
-        } else if (sizeCalibrationString == "geometric") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
-        } else if (sizeCalibrationString == "diameter") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
-        } else if (sizeCalibrationString == "box") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
-        } else if (sizeCalibrationString == "area") {
-            out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
-        } else if (sizeCalibrationString != "default") {
-            ALOGW("Invalid value for touch.size.calibration: '%s'",
-                    sizeCalibrationString.string());
-        }
-    }
-
-    out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"),
-            out.sizeScale);
-    out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"),
-            out.sizeBias);
-    out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"),
-            out.sizeIsSummed);
-
-    // Pressure
-    out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
-    String8 pressureCalibrationString;
-    if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
-        if (pressureCalibrationString == "none") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
-        } else if (pressureCalibrationString == "physical") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
-        } else if (pressureCalibrationString == "amplitude") {
-            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
-        } else if (pressureCalibrationString != "default") {
-            ALOGW("Invalid value for touch.pressure.calibration: '%s'",
-                    pressureCalibrationString.string());
-        }
-    }
-
-    out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
-            out.pressureScale);
-
-    // Orientation
-    out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
-    String8 orientationCalibrationString;
-    if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
-        if (orientationCalibrationString == "none") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
-        } else if (orientationCalibrationString == "interpolated") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
-        } else if (orientationCalibrationString == "vector") {
-            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
-        } else if (orientationCalibrationString != "default") {
-            ALOGW("Invalid value for touch.orientation.calibration: '%s'",
-                    orientationCalibrationString.string());
-        }
-    }
-
-    // Distance
-    out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
-    String8 distanceCalibrationString;
-    if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
-        if (distanceCalibrationString == "none") {
-            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
-        } else if (distanceCalibrationString == "scaled") {
-            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
-        } else if (distanceCalibrationString != "default") {
-            ALOGW("Invalid value for touch.distance.calibration: '%s'",
-                    distanceCalibrationString.string());
-        }
-    }
-
-    out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"),
-            out.distanceScale);
-
-    out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
-    String8 coverageCalibrationString;
-    if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
-        if (coverageCalibrationString == "none") {
-            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
-        } else if (coverageCalibrationString == "box") {
-            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
-        } else if (coverageCalibrationString != "default") {
-            ALOGW("Invalid value for touch.coverage.calibration: '%s'",
-                    coverageCalibrationString.string());
-        }
-    }
-}
-
-void TouchInputMapper::resolveCalibration() {
-    // Size
-    if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
-        if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
-            mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
-        }
-    } else {
-        mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
-    }
-
-    // Pressure
-    if (mRawPointerAxes.pressure.valid) {
-        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
-            mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
-        }
-    } else {
-        mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
-    }
-
-    // Orientation
-    if (mRawPointerAxes.orientation.valid) {
-        if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
-            mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
-        }
-    } else {
-        mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
-    }
-
-    // Distance
-    if (mRawPointerAxes.distance.valid) {
-        if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
-            mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
-        }
-    } else {
-        mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
-    }
-
-    // Coverage
-    if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
-        mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
-    }
-}
-
-void TouchInputMapper::dumpCalibration(std::string& dump) {
-    dump += INDENT3 "Calibration:\n";
-
-    // Size
-    switch (mCalibration.sizeCalibration) {
-    case Calibration::SIZE_CALIBRATION_NONE:
-        dump += INDENT4 "touch.size.calibration: none\n";
-        break;
-    case Calibration::SIZE_CALIBRATION_GEOMETRIC:
-        dump += INDENT4 "touch.size.calibration: geometric\n";
-        break;
-    case Calibration::SIZE_CALIBRATION_DIAMETER:
-        dump += INDENT4 "touch.size.calibration: diameter\n";
-        break;
-    case Calibration::SIZE_CALIBRATION_BOX:
-        dump += INDENT4 "touch.size.calibration: box\n";
-        break;
-    case Calibration::SIZE_CALIBRATION_AREA:
-        dump += INDENT4 "touch.size.calibration: area\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    if (mCalibration.haveSizeScale) {
-        dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n",
-                mCalibration.sizeScale);
-    }
-
-    if (mCalibration.haveSizeBias) {
-        dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n",
-                mCalibration.sizeBias);
-    }
-
-    if (mCalibration.haveSizeIsSummed) {
-        dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n",
-                toString(mCalibration.sizeIsSummed));
-    }
-
-    // Pressure
-    switch (mCalibration.pressureCalibration) {
-    case Calibration::PRESSURE_CALIBRATION_NONE:
-        dump += INDENT4 "touch.pressure.calibration: none\n";
-        break;
-    case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        dump += INDENT4 "touch.pressure.calibration: physical\n";
-        break;
-    case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-        dump += INDENT4 "touch.pressure.calibration: amplitude\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    if (mCalibration.havePressureScale) {
-        dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n",
-                mCalibration.pressureScale);
-    }
-
-    // Orientation
-    switch (mCalibration.orientationCalibration) {
-    case Calibration::ORIENTATION_CALIBRATION_NONE:
-        dump += INDENT4 "touch.orientation.calibration: none\n";
-        break;
-    case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-        dump += INDENT4 "touch.orientation.calibration: interpolated\n";
-        break;
-    case Calibration::ORIENTATION_CALIBRATION_VECTOR:
-        dump += INDENT4 "touch.orientation.calibration: vector\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    // Distance
-    switch (mCalibration.distanceCalibration) {
-    case Calibration::DISTANCE_CALIBRATION_NONE:
-        dump += INDENT4 "touch.distance.calibration: none\n";
-        break;
-    case Calibration::DISTANCE_CALIBRATION_SCALED:
-        dump += INDENT4 "touch.distance.calibration: scaled\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-
-    if (mCalibration.haveDistanceScale) {
-        dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n",
-                mCalibration.distanceScale);
-    }
-
-    switch (mCalibration.coverageCalibration) {
-    case Calibration::COVERAGE_CALIBRATION_NONE:
-        dump += INDENT4 "touch.coverage.calibration: none\n";
-        break;
-    case Calibration::COVERAGE_CALIBRATION_BOX:
-        dump += INDENT4 "touch.coverage.calibration: box\n";
-        break;
-    default:
-        ALOG_ASSERT(false);
-    }
-}
-
-void TouchInputMapper::dumpAffineTransformation(std::string& dump) {
-    dump += INDENT3 "Affine Transformation:\n";
-
-    dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale);
-    dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix);
-    dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset);
-    dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix);
-    dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale);
-    dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset);
-}
-
-void TouchInputMapper::updateAffineTransformation() {
-    mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
-            mSurfaceOrientation);
-}
-
-void TouchInputMapper::reset(nsecs_t when) {
-    mCursorButtonAccumulator.reset(getDevice());
-    mCursorScrollAccumulator.reset(getDevice());
-    mTouchButtonAccumulator.reset(getDevice());
-
-    mPointerVelocityControl.reset();
-    mWheelXVelocityControl.reset();
-    mWheelYVelocityControl.reset();
-
-    mRawStatesPending.clear();
-    mCurrentRawState.clear();
-    mCurrentCookedState.clear();
-    mLastRawState.clear();
-    mLastCookedState.clear();
-    mPointerUsage = POINTER_USAGE_NONE;
-    mSentHoverEnter = false;
-    mHavePointerIds = false;
-    mCurrentMotionAborted = false;
-    mDownTime = 0;
-
-    mCurrentVirtualKey.down = false;
-
-    mPointerGesture.reset();
-    mPointerSimple.reset();
-    resetExternalStylus();
-
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        mPointerController->clearSpots();
-    }
-
-    InputMapper::reset(when);
-}
-
-void TouchInputMapper::resetExternalStylus() {
-    mExternalStylusState.clear();
-    mExternalStylusId = -1;
-    mExternalStylusFusionTimeout = LLONG_MAX;
-    mExternalStylusDataPending = false;
-}
-
-void TouchInputMapper::clearStylusDataPendingFlags() {
-    mExternalStylusDataPending = false;
-    mExternalStylusFusionTimeout = LLONG_MAX;
-}
-
-void TouchInputMapper::process(const RawEvent* rawEvent) {
-    mCursorButtonAccumulator.process(rawEvent);
-    mCursorScrollAccumulator.process(rawEvent);
-    mTouchButtonAccumulator.process(rawEvent);
-
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
-    }
-}
-
-void TouchInputMapper::sync(nsecs_t when) {
-    const RawState* last = mRawStatesPending.empty() ?
-            &mCurrentRawState : &mRawStatesPending.back();
-
-    // Push a new state.
-    mRawStatesPending.emplace_back();
-
-    RawState* next = &mRawStatesPending.back();
-    next->clear();
-    next->when = when;
-
-    // Sync button state.
-    next->buttonState = mTouchButtonAccumulator.getButtonState()
-            | mCursorButtonAccumulator.getButtonState();
-
-    // Sync scroll
-    next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
-    next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
-    mCursorScrollAccumulator.finishSync();
-
-    // Sync touch
-    syncTouch(when, next);
-
-    // Assign pointer ids.
-    if (!mHavePointerIds) {
-        assignPointerIds(last, next);
-    }
-
-#if DEBUG_RAW_EVENTS
-    ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
-            "hovering ids 0x%08x -> 0x%08x",
-            last->rawPointerData.pointerCount,
-            next->rawPointerData.pointerCount,
-            last->rawPointerData.touchingIdBits.value,
-            next->rawPointerData.touchingIdBits.value,
-            last->rawPointerData.hoveringIdBits.value,
-            next->rawPointerData.hoveringIdBits.value);
-#endif
-
-    processRawTouches(false /*timeout*/);
-}
-
-void TouchInputMapper::processRawTouches(bool timeout) {
-    if (mDeviceMode == DEVICE_MODE_DISABLED) {
-        // Drop all input if the device is disabled.
-        mCurrentRawState.clear();
-        mRawStatesPending.clear();
-        return;
-    }
-
-    // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
-    // valid and must go through the full cook and dispatch cycle. This ensures that anything
-    // touching the current state will only observe the events that have been dispatched to the
-    // rest of the pipeline.
-    const size_t N = mRawStatesPending.size();
-    size_t count;
-    for(count = 0; count < N; count++) {
-        const RawState& next = mRawStatesPending[count];
-
-        // A failure to assign the stylus id means that we're waiting on stylus data
-        // and so should defer the rest of the pipeline.
-        if (assignExternalStylusId(next, timeout)) {
-            break;
-        }
-
-        // All ready to go.
-        clearStylusDataPendingFlags();
-        mCurrentRawState.copyFrom(next);
-        if (mCurrentRawState.when < mLastRawState.when) {
-            mCurrentRawState.when = mLastRawState.when;
-        }
-        cookAndDispatch(mCurrentRawState.when);
-    }
-    if (count != 0) {
-        mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
-    }
-
-    if (mExternalStylusDataPending) {
-        if (timeout) {
-            nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
-            clearStylusDataPendingFlags();
-            mCurrentRawState.copyFrom(mLastRawState);
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Timeout expired, synthesizing event with new stylus data");
-#endif
-            cookAndDispatch(when);
-        } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
-            mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
-            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
-        }
-    }
-}
-
-void TouchInputMapper::cookAndDispatch(nsecs_t when) {
-    // Always start with a clean state.
-    mCurrentCookedState.clear();
-
-    // Apply stylus buttons to current raw state.
-    applyExternalStylusButtonState(when);
-
-    // Handle policy on initial down or hover events.
-    bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
-            && mCurrentRawState.rawPointerData.pointerCount != 0;
-
-    uint32_t policyFlags = 0;
-    bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
-    if (initialDown || buttonsPressed) {
-        // If this is a touch screen, hide the pointer on an initial down.
-        if (mDeviceMode == DEVICE_MODE_DIRECT) {
-            getContext()->fadePointer();
-        }
-
-        if (mParameters.wake) {
-            policyFlags |= POLICY_FLAG_WAKE;
-        }
-    }
-
-    // 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)) {
-        mCurrentRawState.rawPointerData.clear();
-    }
-
-    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure
-    // with cooked pointer data that has the same ids and indices as the raw data.
-    // The following code can use either the raw or cooked data, as needed.
-    cookPointerData();
-
-    // Apply stylus pressure to current cooked state.
-    applyExternalStylusTouchState(when);
-
-    // Synthesize key down from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-            mViewport.displayId, policyFlags,
-            mLastCookedState.buttonState, mCurrentCookedState.buttonState);
-
-    // Dispatch the touches either directly or by translation through a pointer on screen.
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
-        for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
-                !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& pointer =
-                    mCurrentRawState.rawPointerData.pointerForId(id);
-            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
-                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
-                mCurrentCookedState.stylusIdBits.markBit(id);
-            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
-                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-                mCurrentCookedState.fingerIdBits.markBit(id);
-            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
-                mCurrentCookedState.mouseIdBits.markBit(id);
-            }
-        }
-        for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
-                !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& pointer =
-                    mCurrentRawState.rawPointerData.pointerForId(id);
-            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
-                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
-                mCurrentCookedState.stylusIdBits.markBit(id);
-            }
-        }
-
-        // Stylus takes precedence over all tools, then mouse, then finger.
-        PointerUsage pointerUsage = mPointerUsage;
-        if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
-            mCurrentCookedState.mouseIdBits.clear();
-            mCurrentCookedState.fingerIdBits.clear();
-            pointerUsage = POINTER_USAGE_STYLUS;
-        } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
-            mCurrentCookedState.fingerIdBits.clear();
-            pointerUsage = POINTER_USAGE_MOUSE;
-        } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
-                isPointerDown(mCurrentRawState.buttonState)) {
-            pointerUsage = POINTER_USAGE_GESTURES;
-        }
-
-        dispatchPointerUsage(when, policyFlags, pointerUsage);
-    } else {
-        if (mDeviceMode == DEVICE_MODE_DIRECT
-                && mConfig.showTouches && mPointerController != nullptr) {
-            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-
-            mPointerController->setButtonState(mCurrentRawState.buttonState);
-            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex,
-                    mCurrentCookedState.cookedPointerData.touchingIdBits,
-                    mViewport.displayId);
-        }
-
-        if (!mCurrentMotionAborted) {
-            dispatchButtonRelease(when, policyFlags);
-            dispatchHoverExit(when, policyFlags);
-            dispatchTouches(when, policyFlags);
-            dispatchHoverEnterAndMove(when, policyFlags);
-            dispatchButtonPress(when, policyFlags);
-        }
-
-        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
-            mCurrentMotionAborted = false;
-        }
-    }
-
-    // Synthesize key up from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
-            mViewport.displayId, policyFlags,
-            mLastCookedState.buttonState, mCurrentCookedState.buttonState);
-
-    // Clear some transient state.
-    mCurrentRawState.rawVScroll = 0;
-    mCurrentRawState.rawHScroll = 0;
-
-    // Copy current touch to last touch in preparation for the next cycle.
-    mLastRawState.copyFrom(mCurrentRawState);
-    mLastCookedState.copyFrom(mCurrentCookedState);
-}
-
-void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
-    if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
-        mCurrentRawState.buttonState |= mExternalStylusState.buttons;
-    }
-}
-
-void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
-    CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
-    const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
-
-    if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
-        float pressure = mExternalStylusState.pressure;
-        if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
-            const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
-            pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-        }
-        PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
-        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
-
-        PointerProperties& properties =
-                currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
-        if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-            properties.toolType = mExternalStylusState.toolType;
-        }
-    }
-}
-
-bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
-    if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
-        return false;
-    }
-
-    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
-            && state.rawPointerData.pointerCount != 0;
-    if (initialDown) {
-        if (mExternalStylusState.pressure != 0.0f) {
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Have both stylus and touch data, beginning fusion");
-#endif
-            mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
-        } else if (timeout) {
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Timeout expired, assuming touch is not a stylus.");
-#endif
-            resetExternalStylus();
-        } else {
-            if (mExternalStylusFusionTimeout == LLONG_MAX) {
-                mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
-            }
-#if DEBUG_STYLUS_FUSION
-            ALOGD("No stylus data but stylus is connected, requesting timeout "
-                    "(%" PRId64 "ms)", mExternalStylusFusionTimeout);
-#endif
-            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
-            return true;
-        }
-    }
-
-    // Check if the stylus pointer has gone up.
-    if (mExternalStylusId != -1 &&
-            !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
-#if DEBUG_STYLUS_FUSION
-            ALOGD("Stylus pointer is going up");
-#endif
-        mExternalStylusId = -1;
-    }
-
-    return false;
-}
-
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
-        if (mPointerUsage == POINTER_USAGE_GESTURES) {
-            dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
-        }
-    } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
-        if (mExternalStylusFusionTimeout < when) {
-            processRawTouches(true /*timeout*/);
-        } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
-            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
-        }
-    }
-}
-
-void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
-    mExternalStylusState.copyFrom(state);
-    if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
-        // We're either in the middle of a fused stream of data or we're waiting on data before
-        // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
-        // data.
-        mExternalStylusDataPending = true;
-        processRawTouches(false /*timeout*/);
-    }
-}
-
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
-    // Check for release of a virtual key.
-    if (mCurrentVirtualKey.down) {
-        if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
-            // Pointer went up while virtual key was down.
-            mCurrentVirtualKey.down = false;
-            if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-                ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
-                        mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
-                dispatchVirtualKey(when, policyFlags,
-                        AKEY_EVENT_ACTION_UP,
-                        AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
-            }
-            return true;
-        }
-
-        if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
-            uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
-            const RawPointerData::Pointer& pointer =
-                    mCurrentRawState.rawPointerData.pointerForId(id);
-            const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
-            if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
-                // Pointer is still within the space of the virtual key.
-                return true;
-            }
-        }
-
-        // Pointer left virtual key area or another pointer also went down.
-        // Send key cancellation but do not consume the touch yet.
-        // This is useful when the user swipes through from the virtual key area
-        // into the main display surface.
-        mCurrentVirtualKey.down = false;
-        if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-            ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
-                    mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
-            dispatchVirtualKey(when, policyFlags,
-                    AKEY_EVENT_ACTION_UP,
-                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
-                            | AKEY_EVENT_FLAG_CANCELED);
-        }
-    }
-
-    if (mLastRawState.rawPointerData.touchingIdBits.isEmpty()
-            && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
-        // Pointer just went down.  Check for virtual key press or off-screen touches.
-        uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
-        const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
-        if (!isPointInsideSurface(pointer.x, pointer.y)) {
-            // If exactly one pointer went down, check for virtual key hit.
-            // Otherwise we will drop the entire stroke.
-            if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
-                const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
-                if (virtualKey) {
-                    mCurrentVirtualKey.down = true;
-                    mCurrentVirtualKey.downTime = when;
-                    mCurrentVirtualKey.keyCode = virtualKey->keyCode;
-                    mCurrentVirtualKey.scanCode = virtualKey->scanCode;
-                    mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey(
-                            when, getDevice(), virtualKey->keyCode, virtualKey->scanCode);
-
-                    if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
-                        ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
-                                mCurrentVirtualKey.keyCode,
-                                mCurrentVirtualKey.scanCode);
-#endif
-                        dispatchVirtualKey(when, policyFlags,
-                                AKEY_EVENT_ACTION_DOWN,
-                                AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
-                    }
-                }
-            }
-            return true;
-        }
-    }
-
-    // Disable all virtual key touches that happen within a short time interval of the
-    // most recent touch within the screen area.  The idea is to filter out stray
-    // virtual key presses when interacting with the touch screen.
-    //
-    // Problems we're trying to solve:
-    //
-    // 1. While scrolling a list or dragging the window shade, the user swipes down into a
-    //    virtual key area that is implemented by a separate touch panel and accidentally
-    //    triggers a virtual key.
-    //
-    // 2. While typing in the on screen keyboard, the user taps slightly outside the screen
-    //    area and accidentally triggers a virtual key.  This often happens when virtual keys
-    //    are layed out below the screen near to where the on screen keyboard's space bar
-    //    is displayed.
-    if (mConfig.virtualKeyQuietTime > 0 &&
-            !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
-        mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
-    }
-    return false;
-}
-
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
-        int32_t keyEventAction, int32_t keyEventFlags) {
-    int32_t keyCode = mCurrentVirtualKey.keyCode;
-    int32_t scanCode = mCurrentVirtualKey.scanCode;
-    nsecs_t downTime = mCurrentVirtualKey.downTime;
-    int32_t metaState = mContext->getGlobalMetaState();
-    policyFlags |= POLICY_FLAG_VIRTUAL;
-
-    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, 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) {
-    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,
-                mCurrentCookedState.cookedPointerData.pointerProperties,
-                mCurrentCookedState.cookedPointerData.pointerCoords,
-                mCurrentCookedState.cookedPointerData.idToIndex,
-                currentIdBits, -1,
-                mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-        mCurrentMotionAborted = true;
-    }
-}
-
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
-    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
-    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
-    int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentCookedState.buttonState;
-
-    if (currentIdBits == lastIdBits) {
-        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,
-                    mCurrentCookedState.cookedPointerData.pointerProperties,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex,
-                    currentIdBits, -1,
-                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-        }
-    } else {
-        // There may be pointers going up and pointers going down and pointers moving
-        // all at the same time.
-        BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
-        BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
-        BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
-        BitSet32 dispatchedIdBits(lastIdBits.value);
-
-        // Update last coordinates of pointers that have moved so that we observe the new
-        // pointer positions at the same time as other pointers that have just gone up.
-        bool moveNeeded = updateMovedPointers(
-                mCurrentCookedState.cookedPointerData.pointerProperties,
-                mCurrentCookedState.cookedPointerData.pointerCoords,
-                mCurrentCookedState.cookedPointerData.idToIndex,
-                mLastCookedState.cookedPointerData.pointerProperties,
-                mLastCookedState.cookedPointerData.pointerCoords,
-                mLastCookedState.cookedPointerData.idToIndex,
-                moveIdBits);
-        if (buttonState != mLastCookedState.buttonState) {
-            moveNeeded = true;
-        }
-
-        // Dispatch pointer up events.
-        while (!upIdBits.isEmpty()) {
-            uint32_t upId = upIdBits.clearFirstMarkedBit();
-
-            dispatchMotion(when, policyFlags, mSource,
-                    AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
-                    mLastCookedState.cookedPointerData.pointerProperties,
-                    mLastCookedState.cookedPointerData.pointerCoords,
-                    mLastCookedState.cookedPointerData.idToIndex,
-                    dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-            dispatchedIdBits.clearBit(upId);
-        }
-
-        // Dispatch move events if any of the remaining pointers moved from their old locations.
-        // Although applications receive new locations as part of individual pointer up
-        // 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,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex,
-                    dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-        }
-
-        // Dispatch pointer down events using the new pointer locations.
-        while (!downIdBits.isEmpty()) {
-            uint32_t downId = downIdBits.clearFirstMarkedBit();
-            dispatchedIdBits.markBit(downId);
-
-            if (dispatchedIdBits.count() == 1) {
-                // First pointer is going down.  Set down time.
-                mDownTime = when;
-            }
-
-            dispatchMotion(when, policyFlags, mSource,
-                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
-                    mCurrentCookedState.cookedPointerData.pointerProperties,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex,
-                    dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-        }
-    }
-}
-
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, 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,
-                mLastCookedState.cookedPointerData.pointerProperties,
-                mLastCookedState.cookedPointerData.pointerCoords,
-                mLastCookedState.cookedPointerData.idToIndex,
-                mLastCookedState.cookedPointerData.hoveringIdBits, -1,
-                mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-        mSentHoverEnter = false;
-    }
-}
-
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, 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,
-                    mCurrentCookedState.cookedPointerData.pointerProperties,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex,
-                    mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-            mSentHoverEnter = true;
-        }
-
-        dispatchMotion(when, policyFlags, mSource,
-                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                mCurrentRawState.buttonState, 0,
-                mCurrentCookedState.cookedPointerData.pointerProperties,
-                mCurrentCookedState.cookedPointerData.pointerCoords,
-                mCurrentCookedState.cookedPointerData.idToIndex,
-                mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-    }
-}
-
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
-    BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
-    const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
-    const int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mLastCookedState.buttonState;
-    while (!releasedButtons.isEmpty()) {
-        int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
-        buttonState &= ~actionButton;
-        dispatchMotion(when, policyFlags, mSource,
-                    AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton,
-                    0, metaState, buttonState, 0,
-                    mCurrentCookedState.cookedPointerData.pointerProperties,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-    }
-}
-
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
-    BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
-    const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
-    const int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mLastCookedState.buttonState;
-    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,
-                    mCurrentCookedState.cookedPointerData.pointerProperties,
-                    mCurrentCookedState.cookedPointerData.pointerCoords,
-                    mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
-    }
-}
-
-const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
-    if (!cookedPointerData.touchingIdBits.isEmpty()) {
-        return cookedPointerData.touchingIdBits;
-    }
-    return cookedPointerData.hoveringIdBits;
-}
-
-void TouchInputMapper::cookPointerData() {
-    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
-
-    mCurrentCookedState.cookedPointerData.clear();
-    mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
-    mCurrentCookedState.cookedPointerData.hoveringIdBits =
-            mCurrentRawState.rawPointerData.hoveringIdBits;
-    mCurrentCookedState.cookedPointerData.touchingIdBits =
-            mCurrentRawState.rawPointerData.touchingIdBits;
-
-    if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
-        mCurrentCookedState.buttonState = 0;
-    } else {
-        mCurrentCookedState.buttonState = mCurrentRawState.buttonState;
-    }
-
-    // Walk through the the active pointers and map device coordinates onto
-    // surface coordinates and adjust for display orientation.
-    for (uint32_t i = 0; i < currentPointerCount; i++) {
-        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
-
-        // Size
-        float touchMajor, touchMinor, toolMajor, toolMinor, size;
-        switch (mCalibration.sizeCalibration) {
-        case Calibration::SIZE_CALIBRATION_GEOMETRIC:
-        case Calibration::SIZE_CALIBRATION_DIAMETER:
-        case Calibration::SIZE_CALIBRATION_BOX:
-        case Calibration::SIZE_CALIBRATION_AREA:
-            if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
-                touchMajor = in.touchMajor;
-                touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
-                toolMajor = in.toolMajor;
-                toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
-                size = mRawPointerAxes.touchMinor.valid
-                        ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
-            } else if (mRawPointerAxes.touchMajor.valid) {
-                toolMajor = touchMajor = in.touchMajor;
-                toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid
-                        ? in.touchMinor : in.touchMajor;
-                size = mRawPointerAxes.touchMinor.valid
-                        ? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
-            } else if (mRawPointerAxes.toolMajor.valid) {
-                touchMajor = toolMajor = in.toolMajor;
-                touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid
-                        ? in.toolMinor : in.toolMajor;
-                size = mRawPointerAxes.toolMinor.valid
-                        ? avg(in.toolMajor, in.toolMinor) : in.toolMajor;
-            } else {
-                ALOG_ASSERT(false, "No touch or tool axes.  "
-                        "Size calibration should have been resolved to NONE.");
-                touchMajor = 0;
-                touchMinor = 0;
-                toolMajor = 0;
-                toolMinor = 0;
-                size = 0;
-            }
-
-            if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
-                uint32_t touchingCount =
-                        mCurrentRawState.rawPointerData.touchingIdBits.count();
-                if (touchingCount > 1) {
-                    touchMajor /= touchingCount;
-                    touchMinor /= touchingCount;
-                    toolMajor /= touchingCount;
-                    toolMinor /= touchingCount;
-                    size /= touchingCount;
-                }
-            }
-
-            if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
-                touchMajor *= mGeometricScale;
-                touchMinor *= mGeometricScale;
-                toolMajor *= mGeometricScale;
-                toolMinor *= mGeometricScale;
-            } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
-                touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
-                touchMinor = touchMajor;
-                toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
-                toolMinor = toolMajor;
-            } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
-                touchMinor = touchMajor;
-                toolMinor = toolMajor;
-            }
-
-            mCalibration.applySizeScaleAndBias(&touchMajor);
-            mCalibration.applySizeScaleAndBias(&touchMinor);
-            mCalibration.applySizeScaleAndBias(&toolMajor);
-            mCalibration.applySizeScaleAndBias(&toolMinor);
-            size *= mSizeScale;
-            break;
-        default:
-            touchMajor = 0;
-            touchMinor = 0;
-            toolMajor = 0;
-            toolMinor = 0;
-            size = 0;
-            break;
-        }
-
-        // Pressure
-        float pressure;
-        switch (mCalibration.pressureCalibration) {
-        case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-            pressure = in.pressure * mPressureScale;
-            break;
-        default:
-            pressure = in.isHovering ? 0 : 1;
-            break;
-        }
-
-        // Tilt and Orientation
-        float tilt;
-        float orientation;
-        if (mHaveTilt) {
-            float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
-            float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
-            orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
-            tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
-        } else {
-            tilt = 0;
-
-            switch (mCalibration.orientationCalibration) {
-            case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-                orientation = in.orientation * mOrientationScale;
-                break;
-            case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
-                int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
-                int32_t c2 = signExtendNybble(in.orientation & 0x0f);
-                if (c1 != 0 || c2 != 0) {
-                    orientation = atan2f(c1, c2) * 0.5f;
-                    float confidence = hypotf(c1, c2);
-                    float scale = 1.0f + confidence / 16.0f;
-                    touchMajor *= scale;
-                    touchMinor /= scale;
-                    toolMajor *= scale;
-                    toolMinor /= scale;
-                } else {
-                    orientation = 0;
-                }
-                break;
-            }
-            default:
-                orientation = 0;
-            }
-        }
-
-        // Distance
-        float distance;
-        switch (mCalibration.distanceCalibration) {
-        case Calibration::DISTANCE_CALIBRATION_SCALED:
-            distance = in.distance * mDistanceScale;
-            break;
-        default:
-            distance = 0;
-        }
-
-        // Coverage
-        int32_t rawLeft, rawTop, rawRight, rawBottom;
-        switch (mCalibration.coverageCalibration) {
-        case Calibration::COVERAGE_CALIBRATION_BOX:
-            rawLeft = (in.toolMinor & 0xffff0000) >> 16;
-            rawRight = in.toolMinor & 0x0000ffff;
-            rawBottom = in.toolMajor & 0x0000ffff;
-            rawTop = (in.toolMajor & 0xffff0000) >> 16;
-            break;
-        default:
-            rawLeft = rawTop = rawRight = rawBottom = 0;
-            break;
-        }
-
-        // Adjust X,Y coords for device calibration
-        // TODO: Adjust coverage coords?
-        float xTransformed = in.x, yTransformed = in.y;
-        mAffineTransform.applyTo(xTransformed, yTransformed);
-
-        // Adjust X, Y, and coverage coords for surface orientation.
-        float x, y;
-        float left, top, right, bottom;
-
-        switch (mSurfaceOrientation) {
-        case DISPLAY_ORIENTATION_90:
-            x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
-            left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
-            top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
-            orientation -= M_PI_2;
-            if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
-                orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
-            }
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
-            y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
-            left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
-            right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
-            bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
-            top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
-            orientation -= M_PI;
-            if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
-                orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
-            }
-            break;
-        case DISPLAY_ORIENTATION_270:
-            x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
-            y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
-            right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
-            bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            orientation += M_PI_2;
-            if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) {
-                orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
-            }
-            break;
-        default:
-            x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-            bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-            break;
-        }
-
-        // Write output coords.
-        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
-        out.clear();
-        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
-        out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
-        out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
-        out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
-        out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
-        out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
-        out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
-        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
-            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
-            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
-            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
-            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
-        } else {
-            out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
-            out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
-        }
-
-        // Write output properties.
-        PointerProperties& properties =
-                mCurrentCookedState.cookedPointerData.pointerProperties[i];
-        uint32_t id = in.id;
-        properties.clear();
-        properties.id = id;
-        properties.toolType = in.toolType;
-
-        // Write id index.
-        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
-    }
-}
-
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
-        PointerUsage pointerUsage) {
-    if (pointerUsage != mPointerUsage) {
-        abortPointerUsage(when, policyFlags);
-        mPointerUsage = pointerUsage;
-    }
-
-    switch (mPointerUsage) {
-    case POINTER_USAGE_GESTURES:
-        dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
-        break;
-    case POINTER_USAGE_STYLUS:
-        dispatchPointerStylus(when, policyFlags);
-        break;
-    case POINTER_USAGE_MOUSE:
-        dispatchPointerMouse(when, policyFlags);
-        break;
-    default:
-        break;
-    }
-}
-
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
-    switch (mPointerUsage) {
-    case POINTER_USAGE_GESTURES:
-        abortPointerGestures(when, policyFlags);
-        break;
-    case POINTER_USAGE_STYLUS:
-        abortPointerStylus(when, policyFlags);
-        break;
-    case POINTER_USAGE_MOUSE:
-        abortPointerMouse(when, policyFlags);
-        break;
-    default:
-        break;
-    }
-
-    mPointerUsage = POINTER_USAGE_NONE;
-}
-
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
-        bool isTimeout) {
-    // Update current gesture coordinates.
-    bool cancelPreviousGesture, finishPreviousGesture;
-    bool sendEvents = preparePointerGestures(when,
-            &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
-    if (!sendEvents) {
-        return;
-    }
-    if (finishPreviousGesture) {
-        cancelPreviousGesture = false;
-    }
-
-    // Update the pointer presentation and spots.
-    if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
-        if (finishPreviousGesture || cancelPreviousGesture) {
-            mPointerController->clearSpots();
-        }
-
-        if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
-            mPointerController->setSpots(mPointerGesture.currentGestureCoords,
-                     mPointerGesture.currentGestureIdToIndex,
-                     mPointerGesture.currentGestureIdBits,
-                     mPointerController->getDisplayId());
-        }
-    } else {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
-    }
-
-    // Show or hide the pointer if needed.
-    switch (mPointerGesture.currentGestureMode) {
-    case PointerGesture::NEUTRAL:
-    case PointerGesture::QUIET:
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH
-                && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
-            // Remind the user of where the pointer is after finishing a gesture with spots.
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
-        }
-        break;
-    case PointerGesture::TAP:
-    case PointerGesture::TAP_DRAG:
-    case PointerGesture::BUTTON_CLICK_OR_DRAG:
-    case PointerGesture::HOVER:
-    case PointerGesture::PRESS:
-    case PointerGesture::SWIPE:
-        // Unfade the pointer when the current gesture manipulates the
-        // area directly under the pointer.
-        mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-        break;
-    case PointerGesture::FREEFORM:
-        // Fade the pointer when the current gesture manipulates a different
-        // area and there are spots to guide the user experience.
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        } else {
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-        }
-        break;
-    }
-
-    // Send events!
-    int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentCookedState.buttonState;
-
-    // Update last coordinates of pointers that have moved so that we observe the new
-    // pointer positions at the same time as other pointers that have just gone up.
-    bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP
-            || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG
-            || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
-            || mPointerGesture.currentGestureMode == PointerGesture::PRESS
-            || mPointerGesture.currentGestureMode == PointerGesture::SWIPE
-            || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
-    bool moveNeeded = false;
-    if (down && !cancelPreviousGesture && !finishPreviousGesture
-            && !mPointerGesture.lastGestureIdBits.isEmpty()
-            && !mPointerGesture.currentGestureIdBits.isEmpty()) {
-        BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value
-                & mPointerGesture.lastGestureIdBits.value);
-        moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties,
-                mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
-                mPointerGesture.lastGestureProperties,
-                mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
-                movedGestureIdBits);
-        if (buttonState != mLastCookedState.buttonState) {
-            moveNeeded = true;
-        }
-    }
-
-    // Send motion events for all pointers that went up or were canceled.
-    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,
-                           mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                           mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                           mPointerGesture.downTime);
-
-            dispatchedGestureIdBits.clear();
-        } else {
-            BitSet32 upGestureIdBits;
-            if (finishPreviousGesture) {
-                upGestureIdBits = dispatchedGestureIdBits;
-            } else {
-                upGestureIdBits.value = dispatchedGestureIdBits.value
-                        & ~mPointerGesture.currentGestureIdBits.value;
-            }
-            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,
-                        mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
-                        dispatchedGestureIdBits, id,
-                        0, 0, mPointerGesture.downTime);
-
-                dispatchedGestureIdBits.clearBit(id);
-            }
-        }
-    }
-
-    // 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,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                       mPointerGesture.downTime);
-    }
-
-    // Send motion events for all pointers that went down.
-    if (down) {
-        BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value
-                & ~dispatchedGestureIdBits.value);
-        while (!downGestureIdBits.isEmpty()) {
-            uint32_t id = downGestureIdBits.clearFirstMarkedBit();
-            dispatchedGestureIdBits.markBit(id);
-
-            if (dispatchedGestureIdBits.count() == 1) {
-                mPointerGesture.downTime = when;
-            }
-
-            dispatchMotion(when, policyFlags, mSource,
-                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
-                    mPointerGesture.currentGestureProperties,
-                    mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
-                    dispatchedGestureIdBits, id,
-                    0, 0, mPointerGesture.downTime);
-        }
-    }
-
-    // Send motion events for hover.
-    if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex,
-                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
-    } else if (dispatchedGestureIdBits.isEmpty()
-            && !mPointerGesture.lastGestureIdBits.isEmpty()) {
-        // Synthesize a hover move event after all pointers go up to indicate that
-        // the pointer is hovering again even if the user is not currently touching
-        // the touch pad.  This ensures that a view will receive a fresh hover enter
-        // event after a tap.
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-
-        PointerProperties pointerProperties;
-        pointerProperties.clear();
-        pointerProperties.id = 0;
-        pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
-        PointerCoords pointerCoords;
-        pointerCoords.clear();
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-
-        const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), 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 */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    // Update state.
-    mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
-    if (!down) {
-        mPointerGesture.lastGestureIdBits.clear();
-    } else {
-        mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
-        for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
-            mPointerGesture.lastGestureProperties[index].copyFrom(
-                    mPointerGesture.currentGestureProperties[index]);
-            mPointerGesture.lastGestureCoords[index].copyFrom(
-                    mPointerGesture.currentGestureCoords[index]);
-            mPointerGesture.lastGestureIdToIndex[id] = index;
-        }
-    }
-}
-
-void TouchInputMapper::abortPointerGestures(nsecs_t when, 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,
-                       mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                       mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
-                       0, 0, mPointerGesture.downTime);
-    }
-
-    // Reset the current pointer gesture.
-    mPointerGesture.reset();
-    mPointerVelocityControl.reset();
-
-    // Remove any current spots.
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        mPointerController->clearSpots();
-    }
-}
-
-bool TouchInputMapper::preparePointerGestures(nsecs_t when,
-        bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) {
-    *outCancelPreviousGesture = false;
-    *outFinishPreviousGesture = false;
-
-    // Handle TAP timeout.
-    if (isTimeout) {
-#if DEBUG_GESTURES
-        ALOGD("Gestures: Processing timeout");
-#endif
-
-        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
-                // The tap/drag timeout has not yet expired.
-                getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime
-                        + mConfig.pointerGestureTapDragInterval);
-            } else {
-                // The tap is finished.
-#if DEBUG_GESTURES
-                ALOGD("Gestures: TAP finished");
-#endif
-                *outFinishPreviousGesture = true;
-
-                mPointerGesture.activeGestureId = -1;
-                mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
-                mPointerGesture.currentGestureIdBits.clear();
-
-                mPointerVelocityControl.reset();
-                return true;
-            }
-        }
-
-        // We did not handle this timeout.
-        return false;
-    }
-
-    const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
-    const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
-
-    // Update the velocity tracker.
-    {
-        VelocityTracker::Position positions[MAX_POINTERS];
-        uint32_t count = 0;
-        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& pointer =
-                    mCurrentRawState.rawPointerData.pointerForId(id);
-            positions[count].x = pointer.x * mPointerXMovementScale;
-            positions[count].y = pointer.y * mPointerYMovementScale;
-        }
-        mPointerGesture.velocityTracker.addMovement(when,
-                mCurrentCookedState.fingerIdBits, positions);
-    }
-
-    // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
-    // to NEUTRAL, then we should not generate tap event.
-    if (mPointerGesture.lastGestureMode != PointerGesture::HOVER
-            && mPointerGesture.lastGestureMode != PointerGesture::TAP
-            && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
-        mPointerGesture.resetTap();
-    }
-
-    // Pick a new active touch id if needed.
-    // Choose an arbitrary pointer that just went down, if there is one.
-    // Otherwise choose an arbitrary remaining pointer.
-    // This guarantees we always have an active touch id when there is at least one pointer.
-    // We keep the same active touch id for as long as possible.
-    int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
-    int32_t activeTouchId = lastActiveTouchId;
-    if (activeTouchId < 0) {
-        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
-            activeTouchId = mPointerGesture.activeTouchId =
-                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
-            mPointerGesture.firstTouchTime = when;
-        }
-    } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
-        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
-            activeTouchId = mPointerGesture.activeTouchId =
-                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
-        } else {
-            activeTouchId = mPointerGesture.activeTouchId = -1;
-        }
-    }
-
-    // Determine whether we are in quiet time.
-    bool isQuietTime = false;
-    if (activeTouchId < 0) {
-        mPointerGesture.resetQuietTime();
-    } else {
-        isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
-        if (!isQuietTime) {
-            if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
-                    || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
-                    || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)
-                    && currentFingerCount < 2) {
-                // Enter quiet time when exiting swipe or freeform state.
-                // This is to prevent accidentally entering the hover state and flinging the
-                // pointer when finishing a swipe and there is still one pointer left onscreen.
-                isQuietTime = true;
-            } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
-                    && currentFingerCount >= 2
-                    && !isPointerDown(mCurrentRawState.buttonState)) {
-                // Enter quiet time when releasing the button and there are still two or more
-                // fingers down.  This may indicate that one finger was used to press the button
-                // but it has not gone up yet.
-                isQuietTime = true;
-            }
-            if (isQuietTime) {
-                mPointerGesture.quietTime = when;
-            }
-        }
-    }
-
-    // Switch states based on button and pointer state.
-    if (isQuietTime) {
-        // Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
-        ALOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
-                + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
-        if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
-            *outFinishPreviousGesture = true;
-        }
-
-        mPointerGesture.activeGestureId = -1;
-        mPointerGesture.currentGestureMode = PointerGesture::QUIET;
-        mPointerGesture.currentGestureIdBits.clear();
-
-        mPointerVelocityControl.reset();
-    } else if (isPointerDown(mCurrentRawState.buttonState)) {
-        // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
-        // The pointer follows the active touch point.
-        // Emit DOWN, MOVE, UP events at the pointer location.
-        //
-        // Only the active touch matters; other fingers are ignored.  This policy helps
-        // to handle the case where the user places a second finger on the touch pad
-        // to apply the necessary force to depress an integrated button below the surface.
-        // We don't want the second finger to be delivered to applications.
-        //
-        // For this to work well, we need to make sure to track the pointer that is really
-        // active.  If the user first puts one finger down to click then adds another
-        // finger to drag then the active pointer should switch to the finger that is
-        // being dragged.
-#if DEBUG_GESTURES
-        ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
-                "currentFingerCount=%d", activeTouchId, currentFingerCount);
-#endif
-        // Reset state when just starting.
-        if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
-            *outFinishPreviousGesture = true;
-            mPointerGesture.activeGestureId = 0;
-        }
-
-        // Switch pointers if needed.
-        // Find the fastest pointer and follow it.
-        if (activeTouchId >= 0 && currentFingerCount > 1) {
-            int32_t bestId = -1;
-            float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
-            for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.clearFirstMarkedBit();
-                float vx, vy;
-                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
-                    float speed = hypotf(vx, vy);
-                    if (speed > bestSpeed) {
-                        bestId = id;
-                        bestSpeed = speed;
-                    }
-                }
-            }
-            if (bestId >= 0 && bestId != activeTouchId) {
-                mPointerGesture.activeTouchId = activeTouchId = bestId;
-#if DEBUG_GESTURES
-                ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
-                        "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
-#endif
-            }
-        }
-
-        float deltaX = 0, deltaY = 0;
-        if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
-            const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
-            const RawPointerData::Pointer& lastPointer =
-                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
-            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
-            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-            mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-            // Move the pointer using a relative motion.
-            // When using spots, the click will occur at the position of the anchor
-            // spot and all other spots will move there.
-            mPointerController->move(deltaX, deltaY);
-        } else {
-            mPointerVelocityControl.reset();
-        }
-
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-
-        mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
-        mPointerGesture.currentGestureIdBits.clear();
-        mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
-        mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
-        mPointerGesture.currentGestureProperties[0].clear();
-        mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
-        mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-        mPointerGesture.currentGestureCoords[0].clear();
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-    } else if (currentFingerCount == 0) {
-        // Case 3. No fingers down and button is not pressed. (NEUTRAL)
-        if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
-            *outFinishPreviousGesture = true;
-        }
-
-        // Watch for taps coming out of HOVER or TAP_DRAG mode.
-        // Checking for taps after TAP_DRAG allows us to detect double-taps.
-        bool tapped = false;
-        if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
-                || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
-                && lastFingerCount == 1) {
-            if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
-                        && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: TAP");
-#endif
-
-                    mPointerGesture.tapUpTime = when;
-                    getContext()->requestTimeoutAtTime(when
-                            + mConfig.pointerGestureTapDragInterval);
-
-                    mPointerGesture.activeGestureId = 0;
-                    mPointerGesture.currentGestureMode = PointerGesture::TAP;
-                    mPointerGesture.currentGestureIdBits.clear();
-                    mPointerGesture.currentGestureIdBits.markBit(
-                            mPointerGesture.activeGestureId);
-                    mPointerGesture.currentGestureIdToIndex[
-                            mPointerGesture.activeGestureId] = 0;
-                    mPointerGesture.currentGestureProperties[0].clear();
-                    mPointerGesture.currentGestureProperties[0].id =
-                            mPointerGesture.activeGestureId;
-                    mPointerGesture.currentGestureProperties[0].toolType =
-                            AMOTION_EVENT_TOOL_TYPE_FINGER;
-                    mPointerGesture.currentGestureCoords[0].clear();
-                    mPointerGesture.currentGestureCoords[0].setAxisValue(
-                            AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
-                    mPointerGesture.currentGestureCoords[0].setAxisValue(
-                            AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
-                    mPointerGesture.currentGestureCoords[0].setAxisValue(
-                            AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-                    tapped = true;
-                } else {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f",
-                            x - mPointerGesture.tapX,
-                            y - mPointerGesture.tapY);
-#endif
-                }
-            } else {
-#if DEBUG_GESTURES
-                if (mPointerGesture.tapDownTime != LLONG_MIN) {
-                    ALOGD("Gestures: Not a TAP, %0.3fms since down",
-                            (when - mPointerGesture.tapDownTime) * 0.000001f);
-                } else {
-                    ALOGD("Gestures: Not a TAP, incompatible mode transitions");
-                }
-#endif
-            }
-        }
-
-        mPointerVelocityControl.reset();
-
-        if (!tapped) {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: NEUTRAL");
-#endif
-            mPointerGesture.activeGestureId = -1;
-            mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
-            mPointerGesture.currentGestureIdBits.clear();
-        }
-    } else if (currentFingerCount == 1) {
-        // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
-        // The pointer follows the active touch point.
-        // When in HOVER, emit HOVER_MOVE events at the pointer location.
-        // When in TAP_DRAG, emit MOVE events at the pointer location.
-        ALOG_ASSERT(activeTouchId >= 0);
-
-        mPointerGesture.currentGestureMode = PointerGesture::HOVER;
-        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
-            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
-                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop
-                        && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-                    mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
-                } else {
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
-                            x - mPointerGesture.tapX,
-                            y - mPointerGesture.tapY);
-#endif
-                }
-            } else {
-#if DEBUG_GESTURES
-                ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
-                        (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
-            }
-        } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
-            mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
-        }
-
-        float deltaX = 0, deltaY = 0;
-        if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
-            const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
-            const RawPointerData::Pointer& lastPointer =
-                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
-            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
-            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-            mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-            // Move the pointer using a relative motion.
-            // When using spots, the hover or drag will occur at the position of the anchor spot.
-            mPointerController->move(deltaX, deltaY);
-        } else {
-            mPointerVelocityControl.reset();
-        }
-
-        bool down;
-        if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: TAP_DRAG");
-#endif
-            down = true;
-        } else {
-#if DEBUG_GESTURES
-            ALOGD("Gestures: HOVER");
-#endif
-            if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
-                *outFinishPreviousGesture = true;
-            }
-            mPointerGesture.activeGestureId = 0;
-            down = false;
-        }
-
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-
-        mPointerGesture.currentGestureIdBits.clear();
-        mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
-        mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
-        mPointerGesture.currentGestureProperties[0].clear();
-        mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
-        mPointerGesture.currentGestureProperties[0].toolType =
-                AMOTION_EVENT_TOOL_TYPE_FINGER;
-        mPointerGesture.currentGestureCoords[0].clear();
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
-                down ? 1.0f : 0.0f);
-
-        if (lastFingerCount == 0 && currentFingerCount != 0) {
-            mPointerGesture.resetTap();
-            mPointerGesture.tapDownTime = when;
-            mPointerGesture.tapX = x;
-            mPointerGesture.tapY = y;
-        }
-    } else {
-        // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
-        // We need to provide feedback for each finger that goes down so we cannot wait
-        // for the fingers to move before deciding what to do.
-        //
-        // The ambiguous case is deciding what to do when there are two fingers down but they
-        // have not moved enough to determine whether they are part of a drag or part of a
-        // freeform gesture, or just a press or long-press at the pointer location.
-        //
-        // When there are two fingers we start with the PRESS hypothesis and we generate a
-        // down at the pointer location.
-        //
-        // When the two fingers move enough or when additional fingers are added, we make
-        // a decision to transition into SWIPE or FREEFORM mode accordingly.
-        ALOG_ASSERT(activeTouchId >= 0);
-
-        bool settled = when >= mPointerGesture.firstTouchTime
-                + mConfig.pointerGestureMultitouchSettleInterval;
-        if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
-                && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
-                && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
-            *outFinishPreviousGesture = true;
-        } else if (!settled && currentFingerCount > lastFingerCount) {
-            // Additional pointers have gone down but not yet settled.
-            // Reset the gesture.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
-                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
-                            + mConfig.pointerGestureMultitouchSettleInterval - when)
-                            * 0.000001f);
-#endif
-            *outCancelPreviousGesture = true;
-        } else {
-            // Continue previous gesture.
-            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
-        }
-
-        if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
-            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
-            mPointerGesture.activeGestureId = 0;
-            mPointerGesture.referenceIdBits.clear();
-            mPointerVelocityControl.reset();
-
-            // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
-                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
-                            + mConfig.pointerGestureMultitouchSettleInterval - when)
-                            * 0.000001f);
-#endif
-            mCurrentRawState.rawPointerData.getCentroidOfTouchingPointers(
-                    &mPointerGesture.referenceTouchX,
-                    &mPointerGesture.referenceTouchY);
-            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                    &mPointerGesture.referenceGestureY);
-        }
-
-        // Clear the reference deltas for fingers not yet included in the reference calculation.
-        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value
-                & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            mPointerGesture.referenceDeltas[id].dx = 0;
-            mPointerGesture.referenceDeltas[id].dy = 0;
-        }
-        mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
-
-        // Add delta for all fingers and calculate a common movement delta.
-        float commonDeltaX = 0, commonDeltaY = 0;
-        BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value
-                & mCurrentCookedState.fingerIdBits.value);
-        for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
-            bool first = (idBits == commonIdBits);
-            uint32_t id = idBits.clearFirstMarkedBit();
-            const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
-            const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
-            PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-            delta.dx += cpd.x - lpd.x;
-            delta.dy += cpd.y - lpd.y;
-
-            if (first) {
-                commonDeltaX = delta.dx;
-                commonDeltaY = delta.dy;
-            } else {
-                commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
-                commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
-            }
-        }
-
-        // Consider transitions from PRESS to SWIPE or MULTITOUCH.
-        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
-            float dist[MAX_POINTER_ID + 1];
-            int32_t distOverThreshold = 0;
-            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.clearFirstMarkedBit();
-                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                dist[id] = hypotf(delta.dx * mPointerXZoomScale,
-                        delta.dy * mPointerYZoomScale);
-                if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
-                    distOverThreshold += 1;
-                }
-            }
-
-            // Only transition when at least two pointers have moved further than
-            // the minimum distance threshold.
-            if (distOverThreshold >= 2) {
-                if (currentFingerCount > 2) {
-                    // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
-                            currentFingerCount);
-#endif
-                    *outCancelPreviousGesture = true;
-                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-                } else {
-                    // There are exactly two pointers.
-                    BitSet32 idBits(mCurrentCookedState.fingerIdBits);
-                    uint32_t id1 = idBits.clearFirstMarkedBit();
-                    uint32_t id2 = idBits.firstMarkedBit();
-                    const RawPointerData::Pointer& p1 =
-                            mCurrentRawState.rawPointerData.pointerForId(id1);
-                    const RawPointerData::Pointer& p2 =
-                            mCurrentRawState.rawPointerData.pointerForId(id2);
-                    float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
-                    if (mutualDistance > mPointerGestureMaxSwipeWidth) {
-                        // There are two pointers but they are too far apart for a SWIPE,
-                        // switch to FREEFORM.
-#if DEBUG_GESTURES
-                        ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
-                                mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
-                        *outCancelPreviousGesture = true;
-                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-                    } else {
-                        // There are two pointers.  Wait for both pointers to start moving
-                        // before deciding whether this is a SWIPE or FREEFORM gesture.
-                        float dist1 = dist[id1];
-                        float dist2 = dist[id2];
-                        if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
-                                && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
-                            // Calculate the dot product of the displacement vectors.
-                            // When the vectors are oriented in approximately the same direction,
-                            // the angle betweeen them is near zero and the cosine of the angle
-                            // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
-                            PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
-                            PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
-                            float dx1 = delta1.dx * mPointerXZoomScale;
-                            float dy1 = delta1.dy * mPointerYZoomScale;
-                            float dx2 = delta2.dx * mPointerXZoomScale;
-                            float dy2 = delta2.dy * mPointerYZoomScale;
-                            float dot = dx1 * dx2 + dy1 * dy2;
-                            float cosine = dot / (dist1 * dist2); // denominator always > 0
-                            if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
-                                // Pointers are moving in the same direction.  Switch to SWIPE.
-#if DEBUG_GESTURES
-                                ALOGD("Gestures: PRESS transitioned to SWIPE, "
-                                        "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                        "cosine %0.3f >= %0.3f",
-                                        dist1, mConfig.pointerGestureMultitouchMinDistance,
-                                        dist2, mConfig.pointerGestureMultitouchMinDistance,
-                                        cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
-                                mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
-                            } else {
-                                // Pointers are moving in different directions.  Switch to FREEFORM.
-#if DEBUG_GESTURES
-                                ALOGD("Gestures: PRESS transitioned to FREEFORM, "
-                                        "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                        "cosine %0.3f < %0.3f",
-                                        dist1, mConfig.pointerGestureMultitouchMinDistance,
-                                        dist2, mConfig.pointerGestureMultitouchMinDistance,
-                                        cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
-                                *outCancelPreviousGesture = true;
-                                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-                            }
-                        }
-                    }
-                }
-            }
-        } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
-            // Switch from SWIPE to FREEFORM if additional pointers go down.
-            // Cancel previous gesture.
-            if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
-                ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
-                        currentFingerCount);
-#endif
-                *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-            }
-        }
-
-        // Move the reference points based on the overall group motion of the fingers
-        // except in PRESS mode while waiting for a transition to occur.
-        if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
-                && (commonDeltaX || commonDeltaY)) {
-            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.clearFirstMarkedBit();
-                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                delta.dx = 0;
-                delta.dy = 0;
-            }
-
-            mPointerGesture.referenceTouchX += commonDeltaX;
-            mPointerGesture.referenceTouchY += commonDeltaY;
-
-            commonDeltaX *= mPointerXMovementScale;
-            commonDeltaY *= mPointerYMovementScale;
-
-            rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
-            mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
-            mPointerGesture.referenceGestureX += commonDeltaX;
-            mPointerGesture.referenceGestureY += commonDeltaY;
-        }
-
-        // Report gestures.
-        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS
-                || mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
-            // PRESS or SWIPE mode.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
-                    "activeGestureId=%d, currentTouchPointerCount=%d",
-                    activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
-            ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
-            mPointerGesture.currentGestureIdBits.clear();
-            mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
-            mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
-            mPointerGesture.currentGestureProperties[0].clear();
-            mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
-            mPointerGesture.currentGestureProperties[0].toolType =
-                    AMOTION_EVENT_TOOL_TYPE_FINGER;
-            mPointerGesture.currentGestureCoords[0].clear();
-            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
-                    mPointerGesture.referenceGestureX);
-            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
-                    mPointerGesture.referenceGestureY);
-            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-        } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
-            // FREEFORM mode.
-#if DEBUG_GESTURES
-            ALOGD("Gestures: FREEFORM activeTouchId=%d,"
-                    "activeGestureId=%d, currentTouchPointerCount=%d",
-                    activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
-            ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
-            mPointerGesture.currentGestureIdBits.clear();
-
-            BitSet32 mappedTouchIdBits;
-            BitSet32 usedGestureIdBits;
-            if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
-                // Initially, assign the active gesture id to the active touch point
-                // if there is one.  No other touch id bits are mapped yet.
-                if (!*outCancelPreviousGesture) {
-                    mappedTouchIdBits.markBit(activeTouchId);
-                    usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
-                    mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
-                            mPointerGesture.activeGestureId;
-                } else {
-                    mPointerGesture.activeGestureId = -1;
-                }
-            } else {
-                // Otherwise, assume we mapped all touches from the previous frame.
-                // Reuse all mappings that are still applicable.
-                mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value
-                        & mCurrentCookedState.fingerIdBits.value;
-                usedGestureIdBits = mPointerGesture.lastGestureIdBits;
-
-                // Check whether we need to choose a new active gesture id because the
-                // current went went up.
-                for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value
-                        & ~mCurrentCookedState.fingerIdBits.value);
-                        !upTouchIdBits.isEmpty(); ) {
-                    uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
-                    uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
-                    if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
-                        mPointerGesture.activeGestureId = -1;
-                        break;
-                    }
-                }
-            }
-
-#if DEBUG_GESTURES
-            ALOGD("Gestures: FREEFORM follow up "
-                    "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
-                    "activeGestureId=%d",
-                    mappedTouchIdBits.value, usedGestureIdBits.value,
-                    mPointerGesture.activeGestureId);
-#endif
-
-            BitSet32 idBits(mCurrentCookedState.fingerIdBits);
-            for (uint32_t i = 0; i < currentFingerCount; i++) {
-                uint32_t touchId = idBits.clearFirstMarkedBit();
-                uint32_t gestureId;
-                if (!mappedTouchIdBits.hasBit(touchId)) {
-                    gestureId = usedGestureIdBits.markFirstUnmarkedBit();
-                    mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: FREEFORM "
-                            "new mapping for touch id %d -> gesture id %d",
-                            touchId, gestureId);
-#endif
-                } else {
-                    gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
-                    ALOGD("Gestures: FREEFORM "
-                            "existing mapping for touch id %d -> gesture id %d",
-                            touchId, gestureId);
-#endif
-                }
-                mPointerGesture.currentGestureIdBits.markBit(gestureId);
-                mPointerGesture.currentGestureIdToIndex[gestureId] = i;
-
-                const RawPointerData::Pointer& pointer =
-                        mCurrentRawState.rawPointerData.pointerForId(touchId);
-                float deltaX = (pointer.x - mPointerGesture.referenceTouchX)
-                        * mPointerXZoomScale;
-                float deltaY = (pointer.y - mPointerGesture.referenceTouchY)
-                        * mPointerYZoomScale;
-                rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-
-                mPointerGesture.currentGestureProperties[i].clear();
-                mPointerGesture.currentGestureProperties[i].id = gestureId;
-                mPointerGesture.currentGestureProperties[i].toolType =
-                        AMOTION_EVENT_TOOL_TYPE_FINGER;
-                mPointerGesture.currentGestureCoords[i].clear();
-                mPointerGesture.currentGestureCoords[i].setAxisValue(
-                        AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX);
-                mPointerGesture.currentGestureCoords[i].setAxisValue(
-                        AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY);
-                mPointerGesture.currentGestureCoords[i].setAxisValue(
-                        AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-            }
-
-            if (mPointerGesture.activeGestureId < 0) {
-                mPointerGesture.activeGestureId =
-                        mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
-                ALOGD("Gestures: FREEFORM new "
-                        "activeGestureId=%d", mPointerGesture.activeGestureId);
-#endif
-            }
-        }
-    }
-
-    mPointerController->setButtonState(mCurrentRawState.buttonState);
-
-#if DEBUG_GESTURES
-    ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
-            "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
-            "lastGestureMode=%d, lastGestureIdBits=0x%08x",
-            toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
-            mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
-            mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
-    for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
-        const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
-        const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
-        ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
-                "x=%0.3f, y=%0.3f, pressure=%0.3f",
-                id, index, properties.toolType,
-                coords.getAxisValue(AMOTION_EVENT_AXIS_X),
-                coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-    }
-    for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
-        const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
-        const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
-        ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
-                "x=%0.3f, y=%0.3f, pressure=%0.3f",
-                id, index, properties.toolType,
-                coords.getAxisValue(AMOTION_EVENT_AXIS_X),
-                coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-    }
-#endif
-    return true;
-}
-
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
-    mPointerSimple.currentCoords.clear();
-    mPointerSimple.currentProperties.clear();
-
-    bool down, hovering;
-    if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
-        uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
-        uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
-        float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
-        float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
-        mPointerController->setPosition(x, y);
-
-        hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
-        down = !hovering;
-
-        mPointerController->getPosition(&x, &y);
-        mPointerSimple.currentCoords.copyFrom(
-                mCurrentCookedState.cookedPointerData.pointerCoords[index]);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        mPointerSimple.currentProperties.id = 0;
-        mPointerSimple.currentProperties.toolType =
-                mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType;
-    } else {
-        down = false;
-        hovering = false;
-    }
-
-    dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
-    abortPointerSimple(when, policyFlags);
-}
-
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
-    mPointerSimple.currentCoords.clear();
-    mPointerSimple.currentProperties.clear();
-
-    bool down, hovering;
-    if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
-        uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
-        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
-        float deltaX = 0, deltaY = 0;
-        if (mLastCookedState.mouseIdBits.hasBit(id)) {
-            uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
-            deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x
-                    - mLastRawState.rawPointerData.pointers[lastIndex].x)
-                    * mPointerXMovementScale;
-            deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y
-                    - mLastRawState.rawPointerData.pointers[lastIndex].y)
-                    * mPointerYMovementScale;
-
-            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
-            mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-            mPointerController->move(deltaX, deltaY);
-        } else {
-            mPointerVelocityControl.reset();
-        }
-
-        down = isPointerDown(mCurrentRawState.buttonState);
-        hovering = !down;
-
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-        mPointerSimple.currentCoords.copyFrom(
-                mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
-                hovering ? 0.0f : 1.0f);
-        mPointerSimple.currentProperties.id = 0;
-        mPointerSimple.currentProperties.toolType =
-                mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
-    } else {
-        mPointerVelocityControl.reset();
-
-        down = false;
-        hovering = false;
-    }
-
-    dispatchPointerSimple(when, policyFlags, down, hovering);
-}
-
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
-    abortPointerSimple(when, policyFlags);
-
-    mPointerVelocityControl.reset();
-}
-
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
-        bool down, bool hovering) {
-    int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t displayId = mViewport.displayId;
-
-    if (down || hovering) {
-        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
-        mPointerController->clearSpots();
-        mPointerController->setButtonState(mCurrentRawState.buttonState);
-        mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-    } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-    }
-    displayId = mPointerController->getDisplayId();
-
-    float xCursorPosition;
-    float yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
-
-    if (mPointerSimple.down && !down) {
-        mPointerSimple.down = false;
-
-        // Send up.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    if (mPointerSimple.hovering && !hovering) {
-        mPointerSimple.hovering = false;
-
-        // Send hover exit.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    if (down) {
-        if (!mPointerSimple.down) {
-            mPointerSimple.down = true;
-            mPointerSimple.downTime = when;
-
-            // Send down.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
-        }
-
-        // Send move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    if (hovering) {
-        if (!mPointerSimple.hovering) {
-            mPointerSimple.hovering = true;
-
-            // Send hover enter.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
-        }
-
-        // Send hover move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
-        float vscroll = mCurrentRawState.rawVScroll;
-        float hscroll = mCurrentRawState.rawHScroll;
-        mWheelYVelocityControl.move(when, nullptr, &vscroll);
-        mWheelXVelocityControl.move(when, &hscroll, nullptr);
-
-        // Send scroll.
-        PointerCoords pointerCoords;
-        pointerCoords.copyFrom(mPointerSimple.currentCoords);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
-
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
-    }
-
-    // Save state.
-    if (down || hovering) {
-        mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
-        mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
-    } else {
-        mPointerSimple.reset();
-    }
-}
-
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
-    mPointerSimple.currentCoords.clear();
-    mPointerSimple.currentProperties.clear();
-
-    dispatchPointerSimple(when, 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,
-                                      const PointerCoords* coords, const uint32_t* idToIndex,
-                                      BitSet32 idBits, int32_t changedId, float xPrecision,
-                                      float yPrecision, nsecs_t downTime) {
-    PointerCoords pointerCoords[MAX_POINTERS];
-    PointerProperties pointerProperties[MAX_POINTERS];
-    uint32_t pointerCount = 0;
-    while (!idBits.isEmpty()) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t index = idToIndex[id];
-        pointerProperties[pointerCount].copyFrom(properties[index]);
-        pointerCoords[pointerCount].copyFrom(coords[index]);
-
-        if (changedId >= 0 && id == uint32_t(changedId)) {
-            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-        }
-
-        pointerCount += 1;
-    }
-
-    ALOG_ASSERT(pointerCount != 0);
-
-    if (changedId >= 0 && pointerCount == 1) {
-        // Replace initial down and final up action.
-        // We can compare the action without masking off the changed pointer index
-        // because we know the index is 0.
-        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
-            action = AMOTION_EVENT_ACTION_DOWN;
-        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
-            action = AMOTION_EVENT_ACTION_UP;
-        } else {
-            // Can't happen.
-            ALOG_ASSERT(false);
-        }
-    }
-    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-    if (mDeviceMode == DEVICE_MODE_POINTER) {
-        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
-    }
-    const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
-    const int32_t deviceId = getDeviceId();
-    std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
-    std::for_each(frames.begin(), frames.end(),
-            [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId,
-                          policyFlags, action, actionButton, flags, metaState, buttonState,
-                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
-                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
-                          downTime, std::move(frames));
-    getListener()->notifyMotion(&args);
-}
-
-bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
-        const PointerCoords* inCoords, const uint32_t* inIdToIndex,
-        PointerProperties* outProperties, PointerCoords* outCoords, const uint32_t* outIdToIndex,
-        BitSet32 idBits) const {
-    bool changed = false;
-    while (!idBits.isEmpty()) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t inIndex = inIdToIndex[id];
-        uint32_t outIndex = outIdToIndex[id];
-
-        const PointerProperties& curInProperties = inProperties[inIndex];
-        const PointerCoords& curInCoords = inCoords[inIndex];
-        PointerProperties& curOutProperties = outProperties[outIndex];
-        PointerCoords& curOutCoords = outCoords[outIndex];
-
-        if (curInProperties != curOutProperties) {
-            curOutProperties.copyFrom(curInProperties);
-            changed = true;
-        }
-
-        if (curInCoords != curOutCoords) {
-            curOutCoords.copyFrom(curInCoords);
-            changed = true;
-        }
-    }
-    return changed;
-}
-
-void TouchInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-    }
-}
-
-void TouchInputMapper::cancelTouch(nsecs_t when) {
-    abortPointerUsage(when, 0 /*policyFlags*/);
-    abortTouches(when, 0 /* policyFlags*/);
-}
-
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
-    const float scaledX = x * mXScale;
-    const float scaledY = y * mYScale;
-    return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue
-            && scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth
-            && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue
-            && scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight;
-}
-
-const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
-
-    for (const VirtualKey& virtualKey: mVirtualKeys) {
-#if DEBUG_VIRTUAL_KEYS
-        ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
-                "left=%d, top=%d, right=%d, bottom=%d",
-                x, y,
-                virtualKey.keyCode, virtualKey.scanCode,
-                virtualKey.hitLeft, virtualKey.hitTop,
-                virtualKey.hitRight, virtualKey.hitBottom);
-#endif
-
-        if (virtualKey.isHit(x, y)) {
-            return & virtualKey;
-        }
-    }
-
-    return nullptr;
-}
-
-void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
-    uint32_t currentPointerCount = current->rawPointerData.pointerCount;
-    uint32_t lastPointerCount = last->rawPointerData.pointerCount;
-
-    current->rawPointerData.clearIdBits();
-
-    if (currentPointerCount == 0) {
-        // No pointers to assign.
-        return;
-    }
-
-    if (lastPointerCount == 0) {
-        // All pointers are new.
-        for (uint32_t i = 0; i < currentPointerCount; i++) {
-            uint32_t id = i;
-            current->rawPointerData.pointers[i].id = id;
-            current->rawPointerData.idToIndex[id] = i;
-            current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
-        }
-        return;
-    }
-
-    if (currentPointerCount == 1 && lastPointerCount == 1
-            && current->rawPointerData.pointers[0].toolType
-                    == last->rawPointerData.pointers[0].toolType) {
-        // Only one pointer and no change in count so it must have the same id as before.
-        uint32_t id = last->rawPointerData.pointers[0].id;
-        current->rawPointerData.pointers[0].id = id;
-        current->rawPointerData.idToIndex[id] = 0;
-        current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
-        return;
-    }
-
-    // General case.
-    // We build a heap of squared euclidean distances between current and last pointers
-    // associated with the current and last pointer indices.  Then, we find the best
-    // match (by distance) for each current pointer.
-    // The pointers must have the same tool type but it is possible for them to
-    // transition from hovering to touching or vice-versa while retaining the same id.
-    PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
-    uint32_t heapSize = 0;
-    for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
-            currentPointerIndex++) {
-        for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
-                lastPointerIndex++) {
-            const RawPointerData::Pointer& currentPointer =
-                    current->rawPointerData.pointers[currentPointerIndex];
-            const RawPointerData::Pointer& lastPointer =
-                    last->rawPointerData.pointers[lastPointerIndex];
-            if (currentPointer.toolType == lastPointer.toolType) {
-                int64_t deltaX = currentPointer.x - lastPointer.x;
-                int64_t deltaY = currentPointer.y - lastPointer.y;
-
-                uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-                // Insert new element into the heap (sift up).
-                heap[heapSize].currentPointerIndex = currentPointerIndex;
-                heap[heapSize].lastPointerIndex = lastPointerIndex;
-                heap[heapSize].distance = distance;
-                heapSize += 1;
-            }
-        }
-    }
-
-    // Heapify
-    for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
-        startIndex -= 1;
-        for (uint32_t parentIndex = startIndex; ;) {
-            uint32_t childIndex = parentIndex * 2 + 1;
-            if (childIndex >= heapSize) {
-                break;
-            }
-
-            if (childIndex + 1 < heapSize
-                    && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                childIndex += 1;
-            }
-
-            if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                break;
-            }
-
-            swap(heap[parentIndex], heap[childIndex]);
-            parentIndex = childIndex;
-        }
-    }
-
-#if DEBUG_POINTER_ASSIGNMENT
-    ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
-    for (size_t i = 0; i < heapSize; i++) {
-        ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
-                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                heap[i].distance);
-    }
-#endif
-
-    // Pull matches out by increasing order of distance.
-    // To avoid reassigning pointers that have already been matched, the loop keeps track
-    // of which last and current pointers have been matched using the matchedXXXBits variables.
-    // It also tracks the used pointer id bits.
-    BitSet32 matchedLastBits(0);
-    BitSet32 matchedCurrentBits(0);
-    BitSet32 usedIdBits(0);
-    bool first = true;
-    for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
-        while (heapSize > 0) {
-            if (first) {
-                // The first time through the loop, we just consume the root element of
-                // the heap (the one with smallest distance).
-                first = false;
-            } else {
-                // Previous iterations consumed the root element of the heap.
-                // Pop root element off of the heap (sift down).
-                heap[0] = heap[heapSize];
-                for (uint32_t parentIndex = 0; ;) {
-                    uint32_t childIndex = parentIndex * 2 + 1;
-                    if (childIndex >= heapSize) {
-                        break;
-                    }
-
-                    if (childIndex + 1 < heapSize
-                            && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                        childIndex += 1;
-                    }
-
-                    if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                        break;
-                    }
-
-                    swap(heap[parentIndex], heap[childIndex]);
-                    parentIndex = childIndex;
-                }
-
-#if DEBUG_POINTER_ASSIGNMENT
-                ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
-                for (size_t i = 0; i < heapSize; i++) {
-                    ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
-                            i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                            heap[i].distance);
-                }
-#endif
-            }
-
-            heapSize -= 1;
-
-            uint32_t currentPointerIndex = heap[0].currentPointerIndex;
-            if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
-            uint32_t lastPointerIndex = heap[0].lastPointerIndex;
-            if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
-            matchedCurrentBits.markBit(currentPointerIndex);
-            matchedLastBits.markBit(lastPointerIndex);
-
-            uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
-            current->rawPointerData.pointers[currentPointerIndex].id = id;
-            current->rawPointerData.idToIndex[id] = currentPointerIndex;
-            current->rawPointerData.markIdBit(id,
-                    current->rawPointerData.isHovering(currentPointerIndex));
-            usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
-            ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32
-                    ", id=%" PRIu32 ", distance=%" PRIu64,
-                    lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
-            break;
-        }
-    }
-
-    // Assign fresh ids to pointers that were not matched in the process.
-    for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
-        uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
-        uint32_t id = usedIdBits.markFirstUnmarkedBit();
-
-        current->rawPointerData.pointers[currentPointerIndex].id = id;
-        current->rawPointerData.idToIndex[id] = currentPointerIndex;
-        current->rawPointerData.markIdBit(id,
-                current->rawPointerData.isHovering(currentPointerIndex));
-
-#if DEBUG_POINTER_ASSIGNMENT
-        ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
-#endif
-    }
-}
-
-int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
-        return AKEY_STATE_VIRTUAL;
-    }
-
-    for (const VirtualKey& virtualKey : mVirtualKeys) {
-        if (virtualKey.keyCode == keyCode) {
-            return AKEY_STATE_UP;
-        }
-    }
-
-    return AKEY_STATE_UNKNOWN;
-}
-
-int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
-        return AKEY_STATE_VIRTUAL;
-    }
-
-    for (const VirtualKey& virtualKey : mVirtualKeys) {
-        if (virtualKey.scanCode == scanCode) {
-            return AKEY_STATE_UP;
-        }
-    }
-
-    return AKEY_STATE_UNKNOWN;
-}
-
-bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) {
-    for (const VirtualKey& virtualKey : mVirtualKeys) {
-        for (size_t i = 0; i < numCodes; i++) {
-            if (virtualKey.keyCode == keyCodes[i]) {
-                outFlags[i] = 1;
-            }
-        }
-    }
-
-    return true;
-}
-
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
-    if (mParameters.hasAssociatedDisplay) {
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
-            return std::make_optional(mPointerController->getDisplayId());
-        } else {
-            return std::make_optional(mViewport.displayId);
-        }
-    }
-    return std::nullopt;
-}
-
-// --- SingleTouchInputMapper ---
-
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
-        TouchInputMapper(device) {
-}
-
-SingleTouchInputMapper::~SingleTouchInputMapper() {
-}
-
-void SingleTouchInputMapper::reset(nsecs_t when) {
-    mSingleTouchMotionAccumulator.reset(getDevice());
-
-    TouchInputMapper::reset(when);
-}
-
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
-
-    mSingleTouchMotionAccumulator.process(rawEvent);
-}
-
-void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
-    if (mTouchButtonAccumulator.isToolActive()) {
-        outState->rawPointerData.pointerCount = 1;
-        outState->rawPointerData.idToIndex[0] = 0;
-
-        bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
-                && (mTouchButtonAccumulator.isHovering()
-                        || (mRawPointerAxes.pressure.valid
-                                && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
-        outState->rawPointerData.markIdBit(0, isHovering);
-
-        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0];
-        outPointer.id = 0;
-        outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
-        outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
-        outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
-        outPointer.touchMajor = 0;
-        outPointer.touchMinor = 0;
-        outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
-        outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
-        outPointer.orientation = 0;
-        outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();
-        outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();
-        outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();
-        outPointer.toolType = mTouchButtonAccumulator.getToolType();
-        if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-            outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-        }
-        outPointer.isHovering = isHovering;
-    }
-}
-
-void SingleTouchInputMapper::configureRawPointerAxes() {
-    TouchInputMapper::configureRawPointerAxes();
-
-    getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
-    getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
-    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
-    getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
-    getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
-    getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
-    getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
-}
-
-bool SingleTouchInputMapper::hasStylus() const {
-    return mTouchButtonAccumulator.hasStylus();
-}
-
-
-// --- MultiTouchInputMapper ---
-
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
-        TouchInputMapper(device) {
-}
-
-MultiTouchInputMapper::~MultiTouchInputMapper() {
-}
-
-void MultiTouchInputMapper::reset(nsecs_t when) {
-    mMultiTouchMotionAccumulator.reset(getDevice());
-
-    mPointerIdBits.clear();
-
-    TouchInputMapper::reset(when);
-}
-
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
-
-    mMultiTouchMotionAccumulator.process(rawEvent);
-}
-
-void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
-    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
-    size_t outCount = 0;
-    BitSet32 newPointerIdBits;
-    mHavePointerIds = true;
-
-    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
-        const MultiTouchMotionAccumulator::Slot* inSlot =
-                mMultiTouchMotionAccumulator.getSlot(inIndex);
-        if (!inSlot->isInUse()) {
-            continue;
-        }
-
-        if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
-            ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
-                    "ignoring the rest.",
-                    getDeviceName().c_str(), MAX_POINTERS);
-#endif
-            break; // too many fingers!
-        }
-
-        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
-        outPointer.x = inSlot->getX();
-        outPointer.y = inSlot->getY();
-        outPointer.pressure = inSlot->getPressure();
-        outPointer.touchMajor = inSlot->getTouchMajor();
-        outPointer.touchMinor = inSlot->getTouchMinor();
-        outPointer.toolMajor = inSlot->getToolMajor();
-        outPointer.toolMinor = inSlot->getToolMinor();
-        outPointer.orientation = inSlot->getOrientation();
-        outPointer.distance = inSlot->getDistance();
-        outPointer.tiltX = 0;
-        outPointer.tiltY = 0;
-
-        outPointer.toolType = inSlot->getToolType();
-        if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-            outPointer.toolType = mTouchButtonAccumulator.getToolType();
-            if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-                outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-            }
-        }
-
-        bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
-                && (mTouchButtonAccumulator.isHovering()
-                        || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
-        outPointer.isHovering = isHovering;
-
-        // Assign pointer id using tracking id if available.
-        if (mHavePointerIds) {
-            int32_t trackingId = inSlot->getTrackingId();
-            int32_t id = -1;
-            if (trackingId >= 0) {
-                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
-                    uint32_t n = idBits.clearFirstMarkedBit();
-                    if (mPointerTrackingIdMap[n] == trackingId) {
-                        id = n;
-                    }
-                }
-
-                if (id < 0 && !mPointerIdBits.isFull()) {
-                    id = mPointerIdBits.markFirstUnmarkedBit();
-                    mPointerTrackingIdMap[id] = trackingId;
-                }
-            }
-            if (id < 0) {
-                mHavePointerIds = false;
-                outState->rawPointerData.clearIdBits();
-                newPointerIdBits.clear();
-            } else {
-                outPointer.id = id;
-                outState->rawPointerData.idToIndex[id] = outCount;
-                outState->rawPointerData.markIdBit(id, isHovering);
-                newPointerIdBits.markBit(id);
-            }
-        }
-        outCount += 1;
-    }
-
-    outState->rawPointerData.pointerCount = outCount;
-    mPointerIdBits = newPointerIdBits;
-
-    mMultiTouchMotionAccumulator.finishSync();
-}
-
-void MultiTouchInputMapper::configureRawPointerAxes() {
-    TouchInputMapper::configureRawPointerAxes();
-
-    getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
-    getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
-    getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
-    getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
-    getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
-    getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
-    getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
-    getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
-    getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
-    getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
-    getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
-
-    if (mRawPointerAxes.trackingId.valid
-            && mRawPointerAxes.slot.valid
-            && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
-        size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
-        if (slotCount > MAX_SLOTS) {
-            ALOGW("MultiTouch Device %s reported %zu slots but the framework "
-                    "only supports a maximum of %zu slots at this time.",
-                    getDeviceName().c_str(), slotCount, MAX_SLOTS);
-            slotCount = MAX_SLOTS;
-        }
-        mMultiTouchMotionAccumulator.configure(getDevice(),
-                slotCount, true /*usingSlotsProtocol*/);
-    } else {
-        mMultiTouchMotionAccumulator.configure(getDevice(),
-                MAX_POINTERS, false /*usingSlotsProtocol*/);
-    }
-}
-
-bool MultiTouchInputMapper::hasStylus() const {
-    return mMultiTouchMotionAccumulator.hasStylus()
-            || mTouchButtonAccumulator.hasStylus();
-}
-
-// --- ExternalStylusInputMapper
-
-ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) :
-    InputMapper(device) {
-
-}
-
-uint32_t ExternalStylusInputMapper::getSources() {
-    return AINPUT_SOURCE_STYLUS;
-}
-
-void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS,
-            0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
-}
-
-void ExternalStylusInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "External Stylus Input Mapper:\n";
-    dump += INDENT3 "Raw Stylus Axes:\n";
-    dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure");
-    dump += INDENT3 "Stylus State:\n";
-    dumpStylusState(dump, mStylusState);
-}
-
-void ExternalStylusInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
-    mTouchButtonAccumulator.configure(getDevice());
-}
-
-void ExternalStylusInputMapper::reset(nsecs_t when) {
-    InputDevice* device = getDevice();
-    mSingleTouchMotionAccumulator.reset(device);
-    mTouchButtonAccumulator.reset(device);
-    InputMapper::reset(when);
-}
-
-void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
-    mSingleTouchMotionAccumulator.process(rawEvent);
-    mTouchButtonAccumulator.process(rawEvent);
-
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
-    }
-}
-
-void ExternalStylusInputMapper::sync(nsecs_t when) {
-    mStylusState.clear();
-
-    mStylusState.when = when;
-
-    mStylusState.toolType = mTouchButtonAccumulator.getToolType();
-    if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-        mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
-    }
-
-    int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
-    if (mRawPressureAxis.valid) {
-        mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
-    } else if (mTouchButtonAccumulator.isToolActive()) {
-        mStylusState.pressure = 1.0f;
-    } else {
-        mStylusState.pressure = 0.0f;
-    }
-
-    mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
-
-    mContext->dispatchExternalStylusState(mStylusState);
-}
-
-
-// --- JoystickInputMapper ---
-
-JoystickInputMapper::JoystickInputMapper(InputDevice* device) :
-        InputMapper(device) {
-}
-
-JoystickInputMapper::~JoystickInputMapper() {
-}
-
-uint32_t JoystickInputMapper::getSources() {
-    return AINPUT_SOURCE_JOYSTICK;
-}
-
-void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    for (size_t i = 0; i < mAxes.size(); i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        addMotionRange(axis.axisInfo.axis, axis, info);
-
-        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            addMotionRange(axis.axisInfo.highAxis, axis, info);
-
-        }
-    }
-}
-
-void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis,
-        InputDeviceInfo* info) {
-    info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK,
-            axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
-    /* In order to ease the transition for developers from using the old axes
-     * to the newer, more semantically correct axes, we'll continue to register
-     * the old axes as duplicates of their corresponding new ones.  */
-    int32_t compatAxis = getCompatAxis(axisId);
-    if (compatAxis >= 0) {
-        info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK,
-                axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
-    }
-}
-
-/* A mapping from axes the joystick actually has to the axes that should be
- * artificially created for compatibility purposes.
- * Returns -1 if no compatibility axis is needed. */
-int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
-    switch(axis) {
-    case AMOTION_EVENT_AXIS_LTRIGGER:
-        return AMOTION_EVENT_AXIS_BRAKE;
-    case AMOTION_EVENT_AXIS_RTRIGGER:
-        return AMOTION_EVENT_AXIS_GAS;
-    }
-    return -1;
-}
-
-void JoystickInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Joystick Input Mapper:\n";
-
-    dump += INDENT3 "Axes:\n";
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        const char* label = getAxisLabel(axis.axisInfo.axis);
-        if (label) {
-            dump += StringPrintf(INDENT4 "%s", label);
-        } else {
-            dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
-        }
-        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            label = getAxisLabel(axis.axisInfo.highAxis);
-            if (label) {
-                dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
-            } else {
-                dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis,
-                        axis.axisInfo.splitValue);
-            }
-        } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
-            dump += " (invert)";
-        }
-
-        dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
-                axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
-        dump += StringPrintf(INDENT4 "  scale=%0.5f, offset=%0.5f, "
-                "highScale=%0.5f, highOffset=%0.5f\n",
-                axis.scale, axis.offset, axis.highScale, axis.highOffset);
-        dump += StringPrintf(INDENT4 "  rawAxis=%d, rawMin=%d, rawMax=%d, "
-                "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
-                mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
-                axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution);
-    }
-}
-
-void JoystickInputMapper::configure(nsecs_t when,
-        const InputReaderConfiguration* config, uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    if (!changes) { // first time only
-        // Collect all axes.
-        for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
-            if (!(getAbsAxisUsage(abs, getDevice()->getClasses())
-                    & INPUT_DEVICE_CLASS_JOYSTICK)) {
-                continue; // axis must be claimed by a different device
-            }
-
-            RawAbsoluteAxisInfo rawAxisInfo;
-            getAbsoluteAxisInfo(abs, &rawAxisInfo);
-            if (rawAxisInfo.valid) {
-                // Map axis.
-                AxisInfo axisInfo;
-                bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
-                if (!explicitlyMapped) {
-                    // Axis is not explicitly mapped, will choose a generic axis later.
-                    axisInfo.mode = AxisInfo::MODE_NORMAL;
-                    axisInfo.axis = -1;
-                }
-
-                // Apply flat override.
-                int32_t rawFlat = axisInfo.flatOverride < 0
-                        ? rawAxisInfo.flat : axisInfo.flatOverride;
-
-                // Calculate scaling factors and limits.
-                Axis axis;
-                if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
-                    float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
-                    float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                            scale, 0.0f, highScale, 0.0f,
-                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                            rawAxisInfo.resolution * scale);
-                } else if (isCenteredAxis(axisInfo.axis)) {
-                    float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                    float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                            scale, offset, scale, offset,
-                            -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                            rawAxisInfo.resolution * scale);
-                } else {
-                    float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
-                            scale, 0.0f, scale, 0.0f,
-                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
-                            rawAxisInfo.resolution * scale);
-                }
-
-                // To eliminate noise while the joystick is at rest, filter out small variations
-                // in axis values up front.
-                axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
-                mAxes.add(abs, axis);
-            }
-        }
-
-        // If there are too many axes, start dropping them.
-        // Prefer to keep explicitly mapped axes.
-        if (mAxes.size() > PointerCoords::MAX_AXES) {
-            ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
-                    getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
-            pruneAxes(true);
-            pruneAxes(false);
-        }
-
-        // Assign generic axis ids to remaining axes.
-        int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
-        size_t numAxes = mAxes.size();
-        for (size_t i = 0; i < numAxes; i++) {
-            Axis& axis = mAxes.editValueAt(i);
-            if (axis.axisInfo.axis < 0) {
-                while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
-                        && haveAxis(nextGenericAxisId)) {
-                    nextGenericAxisId += 1;
-                }
-
-                if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
-                    axis.axisInfo.axis = nextGenericAxisId;
-                    nextGenericAxisId += 1;
-                } else {
-                    ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
-                            "have already been assigned to other axes.",
-                            getDeviceName().c_str(), mAxes.keyAt(i));
-                    mAxes.removeItemsAt(i--);
-                    numAxes -= 1;
-                }
-            }
-        }
-    }
-}
-
-bool JoystickInputMapper::haveAxis(int32_t axisId) {
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        if (axis.axisInfo.axis == axisId
-                || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT
-                        && axis.axisInfo.highAxis == axisId)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
-    size_t i = mAxes.size();
-    while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
-        if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
-            continue;
-        }
-        ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
-                getDeviceName().c_str(), mAxes.keyAt(i));
-        mAxes.removeItemsAt(i);
-    }
-}
-
-bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
-    switch (axis) {
-    case AMOTION_EVENT_AXIS_X:
-    case AMOTION_EVENT_AXIS_Y:
-    case AMOTION_EVENT_AXIS_Z:
-    case AMOTION_EVENT_AXIS_RX:
-    case AMOTION_EVENT_AXIS_RY:
-    case AMOTION_EVENT_AXIS_RZ:
-    case AMOTION_EVENT_AXIS_HAT_X:
-    case AMOTION_EVENT_AXIS_HAT_Y:
-    case AMOTION_EVENT_AXIS_ORIENTATION:
-    case AMOTION_EVENT_AXIS_RUDDER:
-    case AMOTION_EVENT_AXIS_WHEEL:
-        return true;
-    default:
-        return false;
-    }
-}
-
-void JoystickInputMapper::reset(nsecs_t when) {
-    // Recenter all axes.
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
-        axis.resetValue();
-    }
-
-    InputMapper::reset(when);
-}
-
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
-    switch (rawEvent->type) {
-    case EV_ABS: {
-        ssize_t index = mAxes.indexOfKey(rawEvent->code);
-        if (index >= 0) {
-            Axis& axis = mAxes.editValueAt(index);
-            float newValue, highNewValue;
-            switch (axis.axisInfo.mode) {
-            case AxisInfo::MODE_INVERT:
-                newValue = (axis.rawAxisInfo.maxValue - rawEvent->value)
-                        * axis.scale + axis.offset;
-                highNewValue = 0.0f;
-                break;
-            case AxisInfo::MODE_SPLIT:
-                if (rawEvent->value < axis.axisInfo.splitValue) {
-                    newValue = (axis.axisInfo.splitValue - rawEvent->value)
-                            * axis.scale + axis.offset;
-                    highNewValue = 0.0f;
-                } else if (rawEvent->value > axis.axisInfo.splitValue) {
-                    newValue = 0.0f;
-                    highNewValue = (rawEvent->value - axis.axisInfo.splitValue)
-                            * axis.highScale + axis.highOffset;
-                } else {
-                    newValue = 0.0f;
-                    highNewValue = 0.0f;
-                }
-                break;
-            default:
-                newValue = rawEvent->value * axis.scale + axis.offset;
-                highNewValue = 0.0f;
-                break;
-            }
-            axis.newValue = newValue;
-            axis.highNewValue = highNewValue;
-        }
-        break;
-    }
-
-    case EV_SYN:
-        switch (rawEvent->code) {
-        case SYN_REPORT:
-            sync(rawEvent->when, false /*force*/);
-            break;
-        }
-        break;
-    }
-}
-
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
-    if (!filterAxes(force)) {
-        return;
-    }
-
-    int32_t metaState = mContext->getGlobalMetaState();
-    int32_t buttonState = 0;
-
-    PointerProperties pointerProperties;
-    pointerProperties.clear();
-    pointerProperties.id = 0;
-    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-
-    PointerCoords pointerCoords;
-    pointerCoords.clear();
-
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
-        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
-                    axis.highCurrentValue);
-        }
-    }
-
-    // Moving a joystick axis should not wake the device because joysticks can
-    // be fairly noisy even when not in use.  On the other hand, pushing a gamepad
-    // button will likely wake the device.
-    // TODO: Use the input device configuration to control this behavior more finely.
-    uint32_t policyFlags = 0;
-
-    NotifyMotionArgs args(mContext->getNextSequenceNum(), 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,
-                          &pointerProperties, &pointerCoords, 0, 0,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-    getListener()->notifyMotion(&args);
-}
-
-void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords,
-        int32_t axis, float value) {
-    pointerCoords->setAxisValue(axis, value);
-    /* In order to ease the transition for developers from using the old axes
-     * to the newer, more semantically correct axes, we'll continue to produce
-     * values for the old axes as mirrors of the value of their corresponding
-     * new axes. */
-    int32_t compatAxis = getCompatAxis(axis);
-    if (compatAxis >= 0) {
-        pointerCoords->setAxisValue(compatAxis, value);
-    }
-}
-
-bool JoystickInputMapper::filterAxes(bool force) {
-    bool atLeastOneSignificantChange = force;
-    size_t numAxes = mAxes.size();
-    for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
-        if (force || hasValueChangedSignificantly(axis.filter,
-                axis.newValue, axis.currentValue, axis.min, axis.max)) {
-            axis.currentValue = axis.newValue;
-            atLeastOneSignificantChange = true;
-        }
-        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
-            if (force || hasValueChangedSignificantly(axis.filter,
-                    axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) {
-                axis.highCurrentValue = axis.highNewValue;
-                atLeastOneSignificantChange = true;
-            }
-        }
-    }
-    return atLeastOneSignificantChange;
-}
-
-bool JoystickInputMapper::hasValueChangedSignificantly(
-        float filter, float newValue, float currentValue, float min, float max) {
-    if (newValue != currentValue) {
-        // Filter out small changes in value unless the value is converging on the axis
-        // bounds or center point.  This is intended to reduce the amount of information
-        // sent to applications by particularly noisy joysticks (such as PS3).
-        if (fabs(newValue - currentValue) > filter
-                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min)
-                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max)
-                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(
-        float filter, float newValue, float currentValue, float thresholdValue) {
-    float newDistance = fabs(newValue - thresholdValue);
-    if (newDistance < filter) {
-        float oldDistance = fabs(currentValue - thresholdValue);
-        if (newDistance < oldDistance) {
-            return true;
-        }
-    }
-    return false;
-}
-
-} // namespace android
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
deleted file mode 100644
index 0666ca5..0000000
--- a/services/inputflinger/InputReader.h
+++ /dev/null
@@ -1,1743 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_READER_H
-#define _UI_INPUT_READER_H
-
-#include "EventHub.h"
-#include "PointerControllerInterface.h"
-#include "InputListener.h"
-#include "InputReaderBase.h"
-
-#include <input/DisplayViewport.h>
-#include <input/Input.h>
-#include <input/VelocityControl.h>
-#include <input/VelocityTracker.h>
-#include <ui/DisplayInfo.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-#include <optional>
-#include <stddef.h>
-#include <unistd.h>
-#include <vector>
-
-namespace android {
-
-class InputDevice;
-class InputMapper;
-
-
-struct StylusState {
-    /* Time the stylus event was received. */
-    nsecs_t when;
-    /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
-    float pressure;
-    /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
-    uint32_t buttons;
-    /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
-    int32_t toolType;
-
-    void copyFrom(const StylusState& other) {
-        when = other.when;
-        pressure = other.pressure;
-        buttons = other.buttons;
-        toolType = other.toolType;
-    }
-
-    void clear() {
-        when = LLONG_MAX;
-        pressure = 0.f;
-        buttons = 0;
-        toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-    }
-};
-
-
-/* Internal interface used by individual input devices to access global input device state
- * and parameters maintained by the input reader.
- */
-class InputReaderContext {
-public:
-    InputReaderContext() { }
-    virtual ~InputReaderContext() { }
-
-    virtual void updateGlobalMetaState() = 0;
-    virtual int32_t getGlobalMetaState() = 0;
-
-    virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
-    virtual bool shouldDropVirtualKey(nsecs_t now,
-            InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
-
-    virtual void fadePointer() = 0;
-
-    virtual void requestTimeoutAtTime(nsecs_t when) = 0;
-    virtual int32_t bumpGeneration() = 0;
-
-    virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
-    virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
-
-    virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputListenerInterface* getListener() = 0;
-    virtual EventHubInterface* getEventHub() = 0;
-
-    virtual uint32_t getNextSequenceNum() = 0;
-};
-
-
-/* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input listener.  Some functions of the input reader, such as early
- * event filtering in low power states, are controlled by a separate policy object.
- *
- * The InputReader owns a collection of InputMappers.  Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
- * components running on arbitrary threads.  To keep things manageable, the InputReader
- * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
- * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
- */
-class InputReader : public InputReaderInterface {
-public:
-    InputReader(std::shared_ptr<EventHubInterface> eventHub,
-                const sp<InputReaderPolicyInterface>& policy,
-                const sp<InputListenerInterface>& listener);
-    virtual ~InputReader();
-
-    virtual void dump(std::string& dump);
-    virtual void monitor();
-
-    virtual void loopOnce();
-
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices);
-
-    virtual bool isInputDeviceEnabled(int32_t deviceId);
-
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode);
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode);
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw);
-
-    virtual void toggleCapsLockState(int32_t deviceId);
-
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
-
-    virtual void requestRefreshConfiguration(uint32_t changes);
-
-    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-            ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t deviceId, int32_t token);
-
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
-protected:
-    // These members are protected so they can be instrumented by test cases.
-    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, uint32_t classes);
-
-    class ContextImpl : public InputReaderContext {
-        InputReader* mReader;
-
-    public:
-        explicit ContextImpl(InputReader* reader);
-
-        virtual void updateGlobalMetaState();
-        virtual int32_t getGlobalMetaState();
-        virtual void disableVirtualKeysUntil(nsecs_t time);
-        virtual bool shouldDropVirtualKey(nsecs_t now,
-                InputDevice* device, int32_t keyCode, int32_t scanCode);
-        virtual void fadePointer();
-        virtual void requestTimeoutAtTime(nsecs_t when);
-        virtual int32_t bumpGeneration();
-        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices);
-        virtual void dispatchExternalStylusState(const StylusState& outState);
-        virtual InputReaderPolicyInterface* getPolicy();
-        virtual InputListenerInterface* getListener();
-        virtual EventHubInterface* getEventHub();
-        virtual uint32_t getNextSequenceNum();
-    } mContext;
-
-    friend class ContextImpl;
-
-private:
-    Mutex mLock;
-
-    Condition mReaderIsAliveCondition;
-
-    // This could be unique_ptr, but due to the way InputReader tests are written,
-    // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
-    // in parallel to passing it to the InputReader.
-    std::shared_ptr<EventHubInterface> mEventHub;
-    sp<InputReaderPolicyInterface> mPolicy;
-    sp<QueuedInputListener> mQueuedListener;
-
-    InputReaderConfiguration mConfig;
-
-    // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
-    uint32_t mNextSequenceNum;
-
-    // The event queue.
-    static const int EVENT_BUFFER_SIZE = 256;
-    RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
-
-    KeyedVector<int32_t, InputDevice*> mDevices;
-
-    // low-level input event decoding and device management
-    void processEventsLocked(const RawEvent* rawEvents, size_t count);
-
-    void addDeviceLocked(nsecs_t when, int32_t deviceId);
-    void removeDeviceLocked(nsecs_t when, int32_t deviceId);
-    void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
-    void timeoutExpiredLocked(nsecs_t when);
-
-    void handleConfigurationChangedLocked(nsecs_t when);
-
-    int32_t mGlobalMetaState;
-    void updateGlobalMetaStateLocked();
-    int32_t getGlobalMetaStateLocked();
-
-    void notifyExternalStylusPresenceChanged();
-    void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
-    void dispatchExternalStylusState(const StylusState& state);
-
-    void fadePointerLocked();
-
-    int32_t mGeneration;
-    int32_t bumpGenerationLocked();
-
-    void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
-
-    nsecs_t mDisableVirtualKeysTimeout;
-    void disableVirtualKeysUntilLocked(nsecs_t time);
-    bool shouldDropVirtualKeyLocked(nsecs_t now,
-            InputDevice* device, int32_t keyCode, int32_t scanCode);
-
-    nsecs_t mNextTimeout;
-    void requestTimeoutAtTimeLocked(nsecs_t when);
-
-    uint32_t mConfigurationChangesToRefresh;
-    void refreshConfigurationLocked(uint32_t changes);
-
-    // state queries
-    typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
-    int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
-            GetStateFunc getStateFunc);
-    bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags);
-};
-
-
-/* Represents the state of a single input device. */
-class InputDevice {
-public:
-    InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t
-            controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes);
-    ~InputDevice();
-
-    inline InputReaderContext* getContext() { return mContext; }
-    inline int32_t getId() const { return mId; }
-    inline int32_t getControllerNumber() const { return mControllerNumber; }
-    inline int32_t getGeneration() const { return mGeneration; }
-    inline const std::string getName() const { return mIdentifier.name; }
-    inline const std::string getDescriptor() { return mIdentifier.descriptor; }
-    inline uint32_t getClasses() const { return mClasses; }
-    inline uint32_t getSources() const { return mSources; }
-
-    inline bool isExternal() { return mIsExternal; }
-    inline void setExternal(bool external) { mIsExternal = external; }
-    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
-        return mAssociatedDisplayPort;
-    }
-    inline std::optional<DisplayViewport> getAssociatedViewport() const {
-        return mAssociatedViewport;
-    }
-    inline void setMic(bool hasMic) { mHasMic = hasMic; }
-    inline bool hasMic() const { return mHasMic; }
-
-    inline bool isIgnored() { return mMappers.empty(); }
-
-    bool isEnabled();
-    void setEnabled(bool enabled, nsecs_t when);
-
-    void dump(std::string& dump);
-    void addMapper(InputMapper* mapper);
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    void reset(nsecs_t when);
-    void process(const RawEvent* rawEvents, size_t count);
-    void timeoutExpired(nsecs_t when);
-    void updateExternalStylusState(const StylusState& state);
-
-    void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
-    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-    int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-    bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags);
-    void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
-    void cancelVibrate(int32_t token);
-    void cancelTouch(nsecs_t when);
-
-    int32_t getMetaState();
-    void updateMetaState(int32_t keyCode);
-
-    void fadePointer();
-
-    void bumpGeneration();
-
-    void notifyReset(nsecs_t when);
-
-    inline const PropertyMap& getConfiguration() { return mConfiguration; }
-    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
-    bool hasKey(int32_t code) {
-        return getEventHub()->hasScanCode(mId, code);
-    }
-
-    bool hasAbsoluteAxis(int32_t code) {
-        RawAbsoluteAxisInfo info;
-        getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
-        return info.valid;
-    }
-
-    bool isKeyPressed(int32_t code) {
-        return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
-    }
-
-    int32_t getAbsoluteAxisValue(int32_t code) {
-        int32_t value;
-        getEventHub()->getAbsoluteAxisValue(mId, code, &value);
-        return value;
-    }
-
-    std::optional<int32_t> getAssociatedDisplayId();
-
-private:
-    InputReaderContext* mContext;
-    int32_t mId;
-    int32_t mGeneration;
-    int32_t mControllerNumber;
-    InputDeviceIdentifier mIdentifier;
-    std::string mAlias;
-    uint32_t mClasses;
-
-    std::vector<InputMapper*> mMappers;
-
-    uint32_t mSources;
-    bool mIsExternal;
-    std::optional<uint8_t> mAssociatedDisplayPort;
-    std::optional<DisplayViewport> mAssociatedViewport;
-    bool mHasMic;
-    bool mDropUntilNextSync;
-
-    typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
-    int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
-
-    PropertyMap mConfiguration;
-};
-
-
-/* Keeps track of the state of mouse or touch pad buttons. */
-class CursorButtonAccumulator {
-public:
-    CursorButtonAccumulator();
-    void reset(InputDevice* device);
-
-    void process(const RawEvent* rawEvent);
-
-    uint32_t getButtonState() const;
-
-private:
-    bool mBtnLeft;
-    bool mBtnRight;
-    bool mBtnMiddle;
-    bool mBtnBack;
-    bool mBtnSide;
-    bool mBtnForward;
-    bool mBtnExtra;
-    bool mBtnTask;
-
-    void clearButtons();
-};
-
-
-/* Keeps track of cursor movements. */
-
-class CursorMotionAccumulator {
-public:
-    CursorMotionAccumulator();
-    void reset(InputDevice* device);
-
-    void process(const RawEvent* rawEvent);
-    void finishSync();
-
-    inline int32_t getRelativeX() const { return mRelX; }
-    inline int32_t getRelativeY() const { return mRelY; }
-
-private:
-    int32_t mRelX;
-    int32_t mRelY;
-
-    void clearRelativeAxes();
-};
-
-
-/* Keeps track of cursor scrolling motions. */
-
-class CursorScrollAccumulator {
-public:
-    CursorScrollAccumulator();
-    void configure(InputDevice* device);
-    void reset(InputDevice* device);
-
-    void process(const RawEvent* rawEvent);
-    void finishSync();
-
-    inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
-    inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
-
-    inline int32_t getRelativeX() const { return mRelX; }
-    inline int32_t getRelativeY() const { return mRelY; }
-    inline int32_t getRelativeVWheel() const { return mRelWheel; }
-    inline int32_t getRelativeHWheel() const { return mRelHWheel; }
-
-private:
-    bool mHaveRelWheel;
-    bool mHaveRelHWheel;
-
-    int32_t mRelX;
-    int32_t mRelY;
-    int32_t mRelWheel;
-    int32_t mRelHWheel;
-
-    void clearRelativeAxes();
-};
-
-
-/* Keeps track of the state of touch, stylus and tool buttons. */
-class TouchButtonAccumulator {
-public:
-    TouchButtonAccumulator();
-    void configure(InputDevice* device);
-    void reset(InputDevice* device);
-
-    void process(const RawEvent* rawEvent);
-
-    uint32_t getButtonState() const;
-    int32_t getToolType() const;
-    bool isToolActive() const;
-    bool isHovering() const;
-    bool hasStylus() const;
-
-private:
-    bool mHaveBtnTouch;
-    bool mHaveStylus;
-
-    bool mBtnTouch;
-    bool mBtnStylus;
-    bool mBtnStylus2;
-    bool mBtnToolFinger;
-    bool mBtnToolPen;
-    bool mBtnToolRubber;
-    bool mBtnToolBrush;
-    bool mBtnToolPencil;
-    bool mBtnToolAirbrush;
-    bool mBtnToolMouse;
-    bool mBtnToolLens;
-    bool mBtnToolDoubleTap;
-    bool mBtnToolTripleTap;
-    bool mBtnToolQuadTap;
-
-    void clearButtons();
-};
-
-
-/* Raw axis information from the driver. */
-struct RawPointerAxes {
-    RawAbsoluteAxisInfo x;
-    RawAbsoluteAxisInfo y;
-    RawAbsoluteAxisInfo pressure;
-    RawAbsoluteAxisInfo touchMajor;
-    RawAbsoluteAxisInfo touchMinor;
-    RawAbsoluteAxisInfo toolMajor;
-    RawAbsoluteAxisInfo toolMinor;
-    RawAbsoluteAxisInfo orientation;
-    RawAbsoluteAxisInfo distance;
-    RawAbsoluteAxisInfo tiltX;
-    RawAbsoluteAxisInfo tiltY;
-    RawAbsoluteAxisInfo trackingId;
-    RawAbsoluteAxisInfo slot;
-
-    RawPointerAxes();
-    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
-    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
-    void clear();
-};
-
-
-/* Raw data for a collection of pointers including a pointer id mapping table. */
-struct RawPointerData {
-    struct Pointer {
-        uint32_t id;
-        int32_t x;
-        int32_t y;
-        int32_t pressure;
-        int32_t touchMajor;
-        int32_t touchMinor;
-        int32_t toolMajor;
-        int32_t toolMinor;
-        int32_t orientation;
-        int32_t distance;
-        int32_t tiltX;
-        int32_t tiltY;
-        int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
-        bool isHovering;
-    };
-
-    uint32_t pointerCount;
-    Pointer pointers[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits;
-    uint32_t idToIndex[MAX_POINTER_ID + 1];
-
-    RawPointerData();
-    void clear();
-    void copyFrom(const RawPointerData& other);
-    void getCentroidOfTouchingPointers(float* outX, float* outY) const;
-
-    inline void markIdBit(uint32_t id, bool isHovering) {
-        if (isHovering) {
-            hoveringIdBits.markBit(id);
-        } else {
-            touchingIdBits.markBit(id);
-        }
-    }
-
-    inline void clearIdBits() {
-        hoveringIdBits.clear();
-        touchingIdBits.clear();
-    }
-
-    inline const Pointer& pointerForId(uint32_t id) const {
-        return pointers[idToIndex[id]];
-    }
-
-    inline bool isHovering(uint32_t pointerIndex) {
-        return pointers[pointerIndex].isHovering;
-    }
-};
-
-
-/* Cooked data for a collection of pointers including a pointer id mapping table. */
-struct CookedPointerData {
-    uint32_t pointerCount;
-    PointerProperties pointerProperties[MAX_POINTERS];
-    PointerCoords pointerCoords[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits;
-    uint32_t idToIndex[MAX_POINTER_ID + 1];
-
-    CookedPointerData();
-    void clear();
-    void copyFrom(const CookedPointerData& other);
-
-    inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
-        return pointerCoords[idToIndex[id]];
-    }
-
-    inline PointerCoords& editPointerCoordsWithId(uint32_t id) {
-        return pointerCoords[idToIndex[id]];
-    }
-
-    inline PointerProperties& editPointerPropertiesWithId(uint32_t id) {
-        return pointerProperties[idToIndex[id]];
-    }
-
-    inline bool isHovering(uint32_t pointerIndex) const {
-        return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
-    }
-
-    inline bool isTouching(uint32_t pointerIndex) const {
-        return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
-    }
-};
-
-
-/* Keeps track of the state of single-touch protocol. */
-class SingleTouchMotionAccumulator {
-public:
-    SingleTouchMotionAccumulator();
-
-    void process(const RawEvent* rawEvent);
-    void reset(InputDevice* device);
-
-    inline int32_t getAbsoluteX() const { return mAbsX; }
-    inline int32_t getAbsoluteY() const { return mAbsY; }
-    inline int32_t getAbsolutePressure() const { return mAbsPressure; }
-    inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; }
-    inline int32_t getAbsoluteDistance() const { return mAbsDistance; }
-    inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; }
-    inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; }
-
-private:
-    int32_t mAbsX;
-    int32_t mAbsY;
-    int32_t mAbsPressure;
-    int32_t mAbsToolWidth;
-    int32_t mAbsDistance;
-    int32_t mAbsTiltX;
-    int32_t mAbsTiltY;
-
-    void clearAbsoluteAxes();
-};
-
-
-/* Keeps track of the state of multi-touch protocol. */
-class MultiTouchMotionAccumulator {
-public:
-    class Slot {
-    public:
-        inline bool isInUse() const { return mInUse; }
-        inline int32_t getX() const { return mAbsMTPositionX; }
-        inline int32_t getY() const { return mAbsMTPositionY; }
-        inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
-        inline int32_t getTouchMinor() const {
-            return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; }
-        inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
-        inline int32_t getToolMinor() const {
-            return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; }
-        inline int32_t getOrientation() const { return mAbsMTOrientation; }
-        inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
-        inline int32_t getPressure() const { return mAbsMTPressure; }
-        inline int32_t getDistance() const { return mAbsMTDistance; }
-        inline int32_t getToolType() const;
-
-    private:
-        friend class MultiTouchMotionAccumulator;
-
-        bool mInUse;
-        bool mHaveAbsMTTouchMinor;
-        bool mHaveAbsMTWidthMinor;
-        bool mHaveAbsMTToolType;
-
-        int32_t mAbsMTPositionX;
-        int32_t mAbsMTPositionY;
-        int32_t mAbsMTTouchMajor;
-        int32_t mAbsMTTouchMinor;
-        int32_t mAbsMTWidthMajor;
-        int32_t mAbsMTWidthMinor;
-        int32_t mAbsMTOrientation;
-        int32_t mAbsMTTrackingId;
-        int32_t mAbsMTPressure;
-        int32_t mAbsMTDistance;
-        int32_t mAbsMTToolType;
-
-        Slot();
-        void clear();
-    };
-
-    MultiTouchMotionAccumulator();
-    ~MultiTouchMotionAccumulator();
-
-    void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
-    void reset(InputDevice* device);
-    void process(const RawEvent* rawEvent);
-    void finishSync();
-    bool hasStylus() const;
-
-    inline size_t getSlotCount() const { return mSlotCount; }
-    inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
-
-private:
-    int32_t mCurrentSlot;
-    Slot* mSlots;
-    size_t mSlotCount;
-    bool mUsingSlotsProtocol;
-    bool mHaveStylus;
-
-    void clearSlots(int32_t initialSlot);
-};
-
-
-/* An input mapper transforms raw input events into cooked event data.
- * A single input device can have multiple associated input mappers in order to interpret
- * different classes of events.
- *
- * InputMapper lifecycle:
- * - create
- * - configure with 0 changes
- * - reset
- * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
- * - reset
- * - destroy
- */
-class InputMapper {
-public:
-    explicit InputMapper(InputDevice* device);
-    virtual ~InputMapper();
-
-    inline InputDevice* getDevice() { return mDevice; }
-    inline int32_t getDeviceId() { return mDevice->getId(); }
-    inline const std::string getDeviceName() { return mDevice->getName(); }
-    inline InputReaderContext* getContext() { return mContext; }
-    inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
-    inline InputListenerInterface* getListener() { return mContext->getListener(); }
-    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
-
-    virtual uint32_t getSources() = 0;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent) = 0;
-    virtual void timeoutExpired(nsecs_t when);
-
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags);
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
-            int32_t token);
-    virtual void cancelVibrate(int32_t token);
-    virtual void cancelTouch(nsecs_t when);
-
-    virtual int32_t getMetaState();
-    virtual void updateMetaState(int32_t keyCode);
-
-    virtual void updateExternalStylusState(const StylusState& state);
-
-    virtual void fadePointer();
-    virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
-
-protected:
-    InputDevice* mDevice;
-    InputReaderContext* mContext;
-
-    status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
-    void bumpGeneration();
-
-    static void dumpRawAbsoluteAxisInfo(std::string& dump,
-            const RawAbsoluteAxisInfo& axis, const char* name);
-    static void dumpStylusState(std::string& dump, const StylusState& state);
-};
-
-
-class SwitchInputMapper : public InputMapper {
-public:
-    explicit SwitchInputMapper(InputDevice* device);
-    virtual ~SwitchInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void process(const RawEvent* rawEvent);
-
-    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-    virtual void dump(std::string& dump);
-
-private:
-    uint32_t mSwitchValues;
-    uint32_t mUpdatedSwitchMask;
-
-    void processSwitch(int32_t switchCode, int32_t switchValue);
-    void sync(nsecs_t when);
-};
-
-
-class VibratorInputMapper : public InputMapper {
-public:
-    explicit VibratorInputMapper(InputDevice* device);
-    virtual ~VibratorInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void process(const RawEvent* rawEvent);
-
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
-            int32_t token);
-    virtual void cancelVibrate(int32_t token);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void dump(std::string& dump);
-
-private:
-    bool mVibrating;
-    nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
-    size_t mPatternSize;
-    ssize_t mRepeat;
-    int32_t mToken;
-    ssize_t mIndex;
-    nsecs_t mNextStepTime;
-
-    void nextStep();
-    void stopVibrating();
-};
-
-
-class KeyboardInputMapper : public InputMapper {
-public:
-    KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
-    virtual ~KeyboardInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags);
-
-    virtual int32_t getMetaState();
-    virtual void updateMetaState(int32_t keyCode);
-    virtual std::optional<int32_t> getAssociatedDisplayId();
-
-private:
-    // The current viewport.
-    std::optional<DisplayViewport> mViewport;
-
-    struct KeyDown {
-        int32_t keyCode;
-        int32_t scanCode;
-    };
-
-    uint32_t mSource;
-    int32_t mKeyboardType;
-
-    std::vector<KeyDown> mKeyDowns; // keys that are down
-    int32_t mMetaState;
-    nsecs_t mDownTime; // time of most recent key down
-
-    int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
-
-    struct LedState {
-        bool avail; // led is available
-        bool on;    // we think the led is currently on
-    };
-    LedState mCapsLockLedState;
-    LedState mNumLockLedState;
-    LedState mScrollLockLedState;
-
-    // Immutable configuration parameters.
-    struct Parameters {
-        bool orientationAware;
-        bool handlesKeyRepeat;
-    } mParameters;
-
-    void configureParameters();
-    void dumpParameters(std::string& dump);
-
-    int32_t getOrientation();
-    int32_t getDisplayId();
-
-    bool isKeyboardOrGamepadKey(int32_t scanCode);
-    bool isMediaKey(int32_t keyCode);
-
-    void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
-
-    bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
-
-    ssize_t findKeyDown(int32_t scanCode);
-
-    void resetLedState();
-    void initializeLedState(LedState& ledState, int32_t led);
-    void updateLedState(bool reset);
-    void updateLedStateForModifier(LedState& ledState, int32_t led,
-            int32_t modifier, bool reset);
-    std::optional<DisplayViewport> findViewport(nsecs_t when,
-                                                const InputReaderConfiguration* config);
-};
-
-
-class CursorInputMapper : public InputMapper {
-public:
-    explicit CursorInputMapper(InputDevice* device);
-    virtual ~CursorInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-
-    virtual void fadePointer();
-
-    virtual std::optional<int32_t> getAssociatedDisplayId();
-
-private:
-    // Amount that trackball needs to move in order to generate a key event.
-    static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
-    // Immutable configuration parameters.
-    struct Parameters {
-        enum Mode {
-            MODE_POINTER,
-            MODE_POINTER_RELATIVE,
-            MODE_NAVIGATION,
-        };
-
-        Mode mode;
-        bool hasAssociatedDisplay;
-        bool orientationAware;
-    } mParameters;
-
-    CursorButtonAccumulator mCursorButtonAccumulator;
-    CursorMotionAccumulator mCursorMotionAccumulator;
-    CursorScrollAccumulator mCursorScrollAccumulator;
-
-    int32_t mSource;
-    float mXScale;
-    float mYScale;
-    float mXPrecision;
-    float mYPrecision;
-
-    float mVWheelScale;
-    float mHWheelScale;
-
-    // Velocity controls for mouse pointer and wheel movements.
-    // The controls for X and Y wheel movements are separate to keep them decoupled.
-    VelocityControl mPointerVelocityControl;
-    VelocityControl mWheelXVelocityControl;
-    VelocityControl mWheelYVelocityControl;
-
-    int32_t mOrientation;
-
-    sp<PointerControllerInterface> mPointerController;
-
-    int32_t mButtonState;
-    nsecs_t mDownTime;
-
-    void configureParameters();
-    void dumpParameters(std::string& dump);
-
-    void sync(nsecs_t when);
-};
-
-
-class RotaryEncoderInputMapper : public InputMapper {
-public:
-    explicit RotaryEncoderInputMapper(InputDevice* device);
-    virtual ~RotaryEncoderInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-private:
-    CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
-
-    int32_t mSource;
-    float mScalingFactor;
-    int32_t mOrientation;
-
-    void sync(nsecs_t when);
-};
-
-class TouchInputMapper : public InputMapper {
-public:
-    explicit TouchInputMapper(InputDevice* device);
-    virtual ~TouchInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags);
-
-    virtual void fadePointer();
-    virtual void cancelTouch(nsecs_t when);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void updateExternalStylusState(const StylusState& state);
-    virtual std::optional<int32_t> getAssociatedDisplayId();
-
-protected:
-    CursorButtonAccumulator mCursorButtonAccumulator;
-    CursorScrollAccumulator mCursorScrollAccumulator;
-    TouchButtonAccumulator mTouchButtonAccumulator;
-
-    struct VirtualKey {
-        int32_t keyCode;
-        int32_t scanCode;
-        uint32_t flags;
-
-        // computed hit box, specified in touch screen coords based on known display size
-        int32_t hitLeft;
-        int32_t hitTop;
-        int32_t hitRight;
-        int32_t hitBottom;
-
-        inline bool isHit(int32_t x, int32_t y) const {
-            return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
-        }
-    };
-
-    // Input sources and device mode.
-    uint32_t mSource;
-
-    enum DeviceMode {
-        DEVICE_MODE_DISABLED, // input is disabled
-        DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
-        DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
-        DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
-        DEVICE_MODE_POINTER, // pointer mapping (pointer)
-    };
-    DeviceMode mDeviceMode;
-
-    // The reader's configuration.
-    InputReaderConfiguration mConfig;
-
-    // Immutable configuration parameters.
-    struct Parameters {
-        enum DeviceType {
-            DEVICE_TYPE_TOUCH_SCREEN,
-            DEVICE_TYPE_TOUCH_PAD,
-            DEVICE_TYPE_TOUCH_NAVIGATION,
-            DEVICE_TYPE_POINTER,
-        };
-
-        DeviceType deviceType;
-        bool hasAssociatedDisplay;
-        bool associatedDisplayIsExternal;
-        bool orientationAware;
-        bool hasButtonUnderPad;
-        std::string uniqueDisplayId;
-
-        enum GestureMode {
-            GESTURE_MODE_SINGLE_TOUCH,
-            GESTURE_MODE_MULTI_TOUCH,
-        };
-        GestureMode gestureMode;
-
-        bool wake;
-    } mParameters;
-
-    // Immutable calibration parameters in parsed form.
-    struct Calibration {
-        // Size
-        enum SizeCalibration {
-            SIZE_CALIBRATION_DEFAULT,
-            SIZE_CALIBRATION_NONE,
-            SIZE_CALIBRATION_GEOMETRIC,
-            SIZE_CALIBRATION_DIAMETER,
-            SIZE_CALIBRATION_BOX,
-            SIZE_CALIBRATION_AREA,
-        };
-
-        SizeCalibration sizeCalibration;
-
-        bool haveSizeScale;
-        float sizeScale;
-        bool haveSizeBias;
-        float sizeBias;
-        bool haveSizeIsSummed;
-        bool sizeIsSummed;
-
-        // Pressure
-        enum PressureCalibration {
-            PRESSURE_CALIBRATION_DEFAULT,
-            PRESSURE_CALIBRATION_NONE,
-            PRESSURE_CALIBRATION_PHYSICAL,
-            PRESSURE_CALIBRATION_AMPLITUDE,
-        };
-
-        PressureCalibration pressureCalibration;
-        bool havePressureScale;
-        float pressureScale;
-
-        // Orientation
-        enum OrientationCalibration {
-            ORIENTATION_CALIBRATION_DEFAULT,
-            ORIENTATION_CALIBRATION_NONE,
-            ORIENTATION_CALIBRATION_INTERPOLATED,
-            ORIENTATION_CALIBRATION_VECTOR,
-        };
-
-        OrientationCalibration orientationCalibration;
-
-        // Distance
-        enum DistanceCalibration {
-            DISTANCE_CALIBRATION_DEFAULT,
-            DISTANCE_CALIBRATION_NONE,
-            DISTANCE_CALIBRATION_SCALED,
-        };
-
-        DistanceCalibration distanceCalibration;
-        bool haveDistanceScale;
-        float distanceScale;
-
-        enum CoverageCalibration {
-            COVERAGE_CALIBRATION_DEFAULT,
-            COVERAGE_CALIBRATION_NONE,
-            COVERAGE_CALIBRATION_BOX,
-        };
-
-        CoverageCalibration coverageCalibration;
-
-        inline void applySizeScaleAndBias(float* outSize) const {
-            if (haveSizeScale) {
-                *outSize *= sizeScale;
-            }
-            if (haveSizeBias) {
-                *outSize += sizeBias;
-            }
-            if (*outSize < 0) {
-                *outSize = 0;
-            }
-        }
-    } mCalibration;
-
-    // Affine location transformation/calibration
-    struct TouchAffineTransformation mAffineTransform;
-
-    RawPointerAxes mRawPointerAxes;
-
-    struct RawState {
-        nsecs_t when;
-
-        // Raw pointer sample data.
-        RawPointerData rawPointerData;
-
-        int32_t buttonState;
-
-        // Scroll state.
-        int32_t rawVScroll;
-        int32_t rawHScroll;
-
-        void copyFrom(const RawState& other) {
-            when = other.when;
-            rawPointerData.copyFrom(other.rawPointerData);
-            buttonState = other.buttonState;
-            rawVScroll = other.rawVScroll;
-            rawHScroll = other.rawHScroll;
-        }
-
-        void clear() {
-            when = 0;
-            rawPointerData.clear();
-            buttonState = 0;
-            rawVScroll = 0;
-            rawHScroll = 0;
-        }
-    };
-
-    struct CookedState {
-        // Cooked pointer sample data.
-        CookedPointerData cookedPointerData;
-
-        // Id bits used to differentiate fingers, stylus and mouse tools.
-        BitSet32 fingerIdBits;
-        BitSet32 stylusIdBits;
-        BitSet32 mouseIdBits;
-
-        int32_t buttonState;
-
-        void copyFrom(const CookedState& other) {
-            cookedPointerData.copyFrom(other.cookedPointerData);
-            fingerIdBits = other.fingerIdBits;
-            stylusIdBits = other.stylusIdBits;
-            mouseIdBits = other.mouseIdBits;
-            buttonState = other.buttonState;
-        }
-
-        void clear() {
-            cookedPointerData.clear();
-            fingerIdBits.clear();
-            stylusIdBits.clear();
-            mouseIdBits.clear();
-            buttonState = 0;
-        }
-    };
-
-    std::vector<RawState> mRawStatesPending;
-    RawState mCurrentRawState;
-    CookedState mCurrentCookedState;
-    RawState mLastRawState;
-    CookedState mLastCookedState;
-
-    // State provided by an external stylus
-    StylusState mExternalStylusState;
-    int64_t mExternalStylusId;
-    nsecs_t mExternalStylusFusionTimeout;
-    bool mExternalStylusDataPending;
-
-    // True if we sent a HOVER_ENTER event.
-    bool mSentHoverEnter;
-
-    // Have we assigned pointer IDs for this stream
-    bool mHavePointerIds;
-
-    // Is the current stream of direct touch events aborted
-    bool mCurrentMotionAborted;
-
-    // The time the primary pointer last went down.
-    nsecs_t mDownTime;
-
-    // The pointer controller, or null if the device is not a pointer.
-    sp<PointerControllerInterface> mPointerController;
-
-    std::vector<VirtualKey> mVirtualKeys;
-
-    virtual void configureParameters();
-    virtual void dumpParameters(std::string& dump);
-    virtual void configureRawPointerAxes();
-    virtual void dumpRawPointerAxes(std::string& dump);
-    virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
-    virtual void dumpSurface(std::string& dump);
-    virtual void configureVirtualKeys();
-    virtual void dumpVirtualKeys(std::string& dump);
-    virtual void parseCalibration();
-    virtual void resolveCalibration();
-    virtual void dumpCalibration(std::string& dump);
-    virtual void updateAffineTransformation();
-    virtual void dumpAffineTransformation(std::string& dump);
-    virtual void resolveExternalStylusPresence();
-    virtual bool hasStylus() const = 0;
-    virtual bool hasExternalStylus() const;
-
-    virtual void syncTouch(nsecs_t when, RawState* outState) = 0;
-
-private:
-    // The current viewport.
-    // The components of the viewport are specified in the display's rotated orientation.
-    DisplayViewport mViewport;
-
-    // The surface orientation, width and height set by configureSurface().
-    // The width and height are derived from the viewport but are specified
-    // in the natural orientation.
-    // The surface origin specifies how the surface coordinates should be translated
-    // to align with the logical display coordinate space.
-    int32_t mSurfaceWidth;
-    int32_t mSurfaceHeight;
-    int32_t mSurfaceLeft;
-    int32_t mSurfaceTop;
-
-    // Similar to the surface coordinates, but in the raw display coordinate space rather than in
-    // the logical coordinate space.
-    int32_t mPhysicalWidth;
-    int32_t mPhysicalHeight;
-    int32_t mPhysicalLeft;
-    int32_t mPhysicalTop;
-
-    // The orientation may be different from the viewport orientation as it specifies
-    // the rotation of the surface coordinates required to produce the viewport's
-    // requested orientation, so it will depend on whether the device is orientation aware.
-    int32_t mSurfaceOrientation;
-
-    // Translation and scaling factors, orientation-independent.
-    float mXTranslate;
-    float mXScale;
-    float mXPrecision;
-
-    float mYTranslate;
-    float mYScale;
-    float mYPrecision;
-
-    float mGeometricScale;
-
-    float mPressureScale;
-
-    float mSizeScale;
-
-    float mOrientationScale;
-
-    float mDistanceScale;
-
-    bool mHaveTilt;
-    float mTiltXCenter;
-    float mTiltXScale;
-    float mTiltYCenter;
-    float mTiltYScale;
-
-    bool mExternalStylusConnected;
-
-    // Oriented motion ranges for input device info.
-    struct OrientedRanges {
-        InputDeviceInfo::MotionRange x;
-        InputDeviceInfo::MotionRange y;
-        InputDeviceInfo::MotionRange pressure;
-
-        bool haveSize;
-        InputDeviceInfo::MotionRange size;
-
-        bool haveTouchSize;
-        InputDeviceInfo::MotionRange touchMajor;
-        InputDeviceInfo::MotionRange touchMinor;
-
-        bool haveToolSize;
-        InputDeviceInfo::MotionRange toolMajor;
-        InputDeviceInfo::MotionRange toolMinor;
-
-        bool haveOrientation;
-        InputDeviceInfo::MotionRange orientation;
-
-        bool haveDistance;
-        InputDeviceInfo::MotionRange distance;
-
-        bool haveTilt;
-        InputDeviceInfo::MotionRange tilt;
-
-        OrientedRanges() {
-            clear();
-        }
-
-        void clear() {
-            haveSize = false;
-            haveTouchSize = false;
-            haveToolSize = false;
-            haveOrientation = false;
-            haveDistance = false;
-            haveTilt = false;
-        }
-    } mOrientedRanges;
-
-    // Oriented dimensions and precision.
-    float mOrientedXPrecision;
-    float mOrientedYPrecision;
-
-    struct CurrentVirtualKeyState {
-        bool down;
-        bool ignored;
-        nsecs_t downTime;
-        int32_t keyCode;
-        int32_t scanCode;
-    } mCurrentVirtualKey;
-
-    // Scale factor for gesture or mouse based pointer movements.
-    float mPointerXMovementScale;
-    float mPointerYMovementScale;
-
-    // Scale factor for gesture based zooming and other freeform motions.
-    float mPointerXZoomScale;
-    float mPointerYZoomScale;
-
-    // The maximum swipe width.
-    float mPointerGestureMaxSwipeWidth;
-
-    struct PointerDistanceHeapElement {
-        uint32_t currentPointerIndex : 8;
-        uint32_t lastPointerIndex : 8;
-        uint64_t distance : 48; // squared distance
-    };
-
-    enum PointerUsage {
-        POINTER_USAGE_NONE,
-        POINTER_USAGE_GESTURES,
-        POINTER_USAGE_STYLUS,
-        POINTER_USAGE_MOUSE,
-    };
-    PointerUsage mPointerUsage;
-
-    struct PointerGesture {
-        enum Mode {
-            // No fingers, button is not pressed.
-            // Nothing happening.
-            NEUTRAL,
-
-            // No fingers, button is not pressed.
-            // Tap detected.
-            // Emits DOWN and UP events at the pointer location.
-            TAP,
-
-            // Exactly one finger dragging following a tap.
-            // Pointer follows the active finger.
-            // Emits DOWN, MOVE and UP events at the pointer location.
-            //
-            // Detect double-taps when the finger goes up while in TAP_DRAG mode.
-            TAP_DRAG,
-
-            // Button is pressed.
-            // Pointer follows the active finger if there is one.  Other fingers are ignored.
-            // Emits DOWN, MOVE and UP events at the pointer location.
-            BUTTON_CLICK_OR_DRAG,
-
-            // Exactly one finger, button is not pressed.
-            // Pointer follows the active finger.
-            // Emits HOVER_MOVE events at the pointer location.
-            //
-            // Detect taps when the finger goes up while in HOVER mode.
-            HOVER,
-
-            // Exactly two fingers but neither have moved enough to clearly indicate
-            // whether a swipe or freeform gesture was intended.  We consider the
-            // pointer to be pressed so this enables clicking or long-pressing on buttons.
-            // Pointer does not move.
-            // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
-            PRESS,
-
-            // Exactly two fingers moving in the same direction, button is not pressed.
-            // Pointer does not move.
-            // Emits DOWN, MOVE and UP events with a single pointer coordinate that
-            // follows the midpoint between both fingers.
-            SWIPE,
-
-            // Two or more fingers moving in arbitrary directions, button is not pressed.
-            // Pointer does not move.
-            // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
-            // each finger individually relative to the initial centroid of the finger.
-            FREEFORM,
-
-            // Waiting for quiet time to end before starting the next gesture.
-            QUIET,
-        };
-
-        // Time the first finger went down.
-        nsecs_t firstTouchTime;
-
-        // The active pointer id from the raw touch data.
-        int32_t activeTouchId; // -1 if none
-
-        // The active pointer id from the gesture last delivered to the application.
-        int32_t activeGestureId; // -1 if none
-
-        // Pointer coords and ids for the current and previous pointer gesture.
-        Mode currentGestureMode;
-        BitSet32 currentGestureIdBits;
-        uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
-        PointerProperties currentGestureProperties[MAX_POINTERS];
-        PointerCoords currentGestureCoords[MAX_POINTERS];
-
-        Mode lastGestureMode;
-        BitSet32 lastGestureIdBits;
-        uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
-        PointerProperties lastGestureProperties[MAX_POINTERS];
-        PointerCoords lastGestureCoords[MAX_POINTERS];
-
-        // Time the pointer gesture last went down.
-        nsecs_t downTime;
-
-        // Time when the pointer went down for a TAP.
-        nsecs_t tapDownTime;
-
-        // Time when the pointer went up for a TAP.
-        nsecs_t tapUpTime;
-
-        // Location of initial tap.
-        float tapX, tapY;
-
-        // Time we started waiting for quiescence.
-        nsecs_t quietTime;
-
-        // Reference points for multitouch gestures.
-        float referenceTouchX;    // reference touch X/Y coordinates in surface units
-        float referenceTouchY;
-        float referenceGestureX;  // reference gesture X/Y coordinates in pixels
-        float referenceGestureY;
-
-        // Distance that each pointer has traveled which has not yet been
-        // subsumed into the reference gesture position.
-        BitSet32 referenceIdBits;
-        struct Delta {
-            float dx, dy;
-        };
-        Delta referenceDeltas[MAX_POINTER_ID + 1];
-
-        // Describes how touch ids are mapped to gesture ids for freeform gestures.
-        uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
-
-        // A velocity tracker for determining whether to switch active pointers during drags.
-        VelocityTracker velocityTracker;
-
-        void reset() {
-            firstTouchTime = LLONG_MIN;
-            activeTouchId = -1;
-            activeGestureId = -1;
-            currentGestureMode = NEUTRAL;
-            currentGestureIdBits.clear();
-            lastGestureMode = NEUTRAL;
-            lastGestureIdBits.clear();
-            downTime = 0;
-            velocityTracker.clear();
-            resetTap();
-            resetQuietTime();
-        }
-
-        void resetTap() {
-            tapDownTime = LLONG_MIN;
-            tapUpTime = LLONG_MIN;
-        }
-
-        void resetQuietTime() {
-            quietTime = LLONG_MIN;
-        }
-    } mPointerGesture;
-
-    struct PointerSimple {
-        PointerCoords currentCoords;
-        PointerProperties currentProperties;
-        PointerCoords lastCoords;
-        PointerProperties lastProperties;
-
-        // True if the pointer is down.
-        bool down;
-
-        // True if the pointer is hovering.
-        bool hovering;
-
-        // Time the pointer last went down.
-        nsecs_t downTime;
-
-        void reset() {
-            currentCoords.clear();
-            currentProperties.clear();
-            lastCoords.clear();
-            lastProperties.clear();
-            down = false;
-            hovering = false;
-            downTime = 0;
-        }
-    } mPointerSimple;
-
-    // The pointer and scroll velocity controls.
-    VelocityControl mPointerVelocityControl;
-    VelocityControl mWheelXVelocityControl;
-    VelocityControl mWheelYVelocityControl;
-
-    std::optional<DisplayViewport> findViewport();
-
-    void resetExternalStylus();
-    void clearStylusDataPendingFlags();
-
-    void sync(nsecs_t when);
-
-    bool consumeRawTouches(nsecs_t when, 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 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);
-    const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
-    void cookPointerData();
-    void abortTouches(nsecs_t when, uint32_t policyFlags);
-
-    void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
-    void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
-
-    void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
-    void abortPointerGestures(nsecs_t when, 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 dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
-    void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
-
-    void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
-            bool down, bool hovering);
-    void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
-
-    bool assignExternalStylusId(const RawState& state, bool timeout);
-    void applyExternalStylusButtonState(nsecs_t when);
-    void applyExternalStylusTouchState(nsecs_t when);
-
-    // Dispatches a motion event.
-    // 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, const PointerCoords* coords,
-            const uint32_t* idToIndex, BitSet32 idBits,
-            int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
-
-    // Updates pointer coords and properties for pointers with specified ids that have moved.
-    // Returns true if any of them changed.
-    bool updateMovedPointers(const PointerProperties* inProperties,
-            const PointerCoords* inCoords, const uint32_t* inIdToIndex,
-            PointerProperties* outProperties, PointerCoords* outCoords,
-            const uint32_t* outIdToIndex, BitSet32 idBits) const;
-
-    bool isPointInsideSurface(int32_t x, int32_t y);
-    const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
-
-    static void assignPointerIds(const RawState* last, RawState* current);
-
-    const char* modeToString(DeviceMode deviceMode);
-};
-
-
-class SingleTouchInputMapper : public TouchInputMapper {
-public:
-    explicit SingleTouchInputMapper(InputDevice* device);
-    virtual ~SingleTouchInputMapper();
-
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-protected:
-    virtual void syncTouch(nsecs_t when, RawState* outState);
-    virtual void configureRawPointerAxes();
-    virtual bool hasStylus() const;
-
-private:
-    SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-};
-
-
-class MultiTouchInputMapper : public TouchInputMapper {
-public:
-    explicit MultiTouchInputMapper(InputDevice* device);
-    virtual ~MultiTouchInputMapper();
-
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-protected:
-    virtual void syncTouch(nsecs_t when, RawState* outState);
-    virtual void configureRawPointerAxes();
-    virtual bool hasStylus() const;
-
-private:
-    MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
-
-    // Specifies the pointer id bits that are in use, and their associated tracking id.
-    BitSet32 mPointerIdBits;
-    int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
-};
-
-class ExternalStylusInputMapper : public InputMapper {
-public:
-    explicit ExternalStylusInputMapper(InputDevice* device);
-    virtual ~ExternalStylusInputMapper() = default;
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-    virtual void sync(nsecs_t when);
-
-private:
-    SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-    RawAbsoluteAxisInfo mRawPressureAxis;
-    TouchButtonAccumulator mTouchButtonAccumulator;
-
-    StylusState mStylusState;
-};
-
-
-class JoystickInputMapper : public InputMapper {
-public:
-    explicit JoystickInputMapper(InputDevice* device);
-    virtual ~JoystickInputMapper();
-
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-
-private:
-    struct Axis {
-        RawAbsoluteAxisInfo rawAxisInfo;
-        AxisInfo axisInfo;
-
-        bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
-
-        float scale;   // scale factor from raw to normalized values
-        float offset;  // offset to add after scaling for normalization
-        float highScale;  // scale factor from raw to normalized values of high split
-        float highOffset; // offset to add after scaling for normalization of high split
-
-        float min;        // normalized inclusive minimum
-        float max;        // normalized inclusive maximum
-        float flat;       // normalized flat region size
-        float fuzz;       // normalized error tolerance
-        float resolution; // normalized resolution in units/mm
-
-        float filter;  // filter out small variations of this size
-        float currentValue; // current value
-        float newValue; // most recent value
-        float highCurrentValue; // current value of high split
-        float highNewValue; // most recent value of high split
-
-        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
-                bool explicitlyMapped, float scale, float offset,
-                float highScale, float highOffset,
-                float min, float max, float flat, float fuzz, float resolution) {
-            this->rawAxisInfo = rawAxisInfo;
-            this->axisInfo = axisInfo;
-            this->explicitlyMapped = explicitlyMapped;
-            this->scale = scale;
-            this->offset = offset;
-            this->highScale = highScale;
-            this->highOffset = highOffset;
-            this->min = min;
-            this->max = max;
-            this->flat = flat;
-            this->fuzz = fuzz;
-            this->resolution = resolution;
-            this->filter = 0;
-            resetValue();
-        }
-
-        void resetValue() {
-            this->currentValue = 0;
-            this->newValue = 0;
-            this->highCurrentValue = 0;
-            this->highNewValue = 0;
-        }
-    };
-
-    // Axes indexed by raw ABS_* axis index.
-    KeyedVector<int32_t, Axis> mAxes;
-
-    void sync(nsecs_t when, bool force);
-
-    bool haveAxis(int32_t axisId);
-    void pruneAxes(bool ignoreExplicitlyMappedAxes);
-    bool filterAxes(bool force);
-
-    static bool hasValueChangedSignificantly(float filter,
-            float newValue, float currentValue, float min, float max);
-    static bool hasMovedNearerToValueWithinFilteredRange(float filter,
-            float newValue, float currentValue, float thresholdValue);
-
-    static bool isCenteredAxis(int32_t axis);
-    static int32_t getCompatAxis(int32_t axis);
-
-    static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
-    static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
-            float value);
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_READER_H
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
new file mode 100644
index 0000000..b8c3a80
--- /dev/null
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+    name: "libinputdispatcher",
+    defaults: ["inputflinger_defaults"],
+    srcs: [
+        "Connection.cpp",
+        "Entry.cpp",
+        "InjectionState.cpp",
+        "InputDispatcher.cpp",
+        "InputDispatcherFactory.cpp",
+        "InputDispatcherThread.cpp",
+        "InputState.cpp",
+        "InputTarget.cpp",
+        "Monitor.cpp",
+        "TouchState.cpp"
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libinput",
+        "libinputreporter",
+        "libinputflinger_base",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
new file mode 100644
index 0000000..99e2108
--- /dev/null
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+
+#include <optional>
+
+namespace android::inputdispatcher {
+
+/* Specifies which events are to be canceled and why. */
+struct CancelationOptions {
+    enum Mode {
+        CANCEL_ALL_EVENTS = 0,
+        CANCEL_POINTER_EVENTS = 1,
+        CANCEL_NON_POINTER_EVENTS = 2,
+        CANCEL_FALLBACK_EVENTS = 3,
+    };
+
+    // The criterion to use to determine which events should be canceled.
+    Mode mode;
+
+    // Descriptive reason for the cancelation.
+    const char* reason;
+
+    // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
+    std::optional<int32_t> keyCode = std::nullopt;
+
+    // The specific device id of events to cancel, or nullopt to cancel events from any device.
+    std::optional<int32_t> deviceId = std::nullopt;
+
+    // The specific display id of events to cancel, or nullopt to cancel events on any display.
+    std::optional<int32_t> displayId = std::nullopt;
+
+    CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {}
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
new file mode 100644
index 0000000..6f82f4f
--- /dev/null
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Connection.h"
+
+#include "Entry.h"
+
+namespace android::inputdispatcher {
+
+Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor)
+      : status(STATUS_NORMAL),
+        inputChannel(inputChannel),
+        monitor(monitor),
+        inputPublisher(inputChannel),
+        inputPublisherBlocked(false) {}
+
+Connection::~Connection() {}
+
+const std::string Connection::getWindowName() const {
+    if (inputChannel != nullptr) {
+        return inputChannel->getName();
+    }
+    if (monitor) {
+        return "monitor";
+    }
+    return "?";
+}
+
+const char* Connection::getStatusLabel() const {
+    switch (status) {
+        case STATUS_NORMAL:
+            return "NORMAL";
+        case STATUS_BROKEN:
+            return "BROKEN";
+        case STATUS_ZOMBIE:
+            return "ZOMBIE";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
+    for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
+        if ((*it)->seq == seq) {
+            return it;
+        }
+    }
+    return waitQueue.end();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
new file mode 100644
index 0000000..8423010
--- /dev/null
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+
+#include "InputState.h"
+
+#include <input/InputTransport.h>
+#include <deque>
+
+namespace android::inputdispatcher {
+
+struct DispatchEntry;
+
+/* Manages the dispatch state associated with a single input channel. */
+class Connection : public RefBase {
+protected:
+    virtual ~Connection();
+
+public:
+    enum Status {
+        // Everything is peachy.
+        STATUS_NORMAL,
+        // An unrecoverable communication error has occurred.
+        STATUS_BROKEN,
+        // The input channel has been unregistered.
+        STATUS_ZOMBIE
+    };
+
+    Status status;
+    sp<InputChannel> inputChannel; // never null
+    bool monitor;
+    InputPublisher inputPublisher;
+    InputState inputState;
+
+    // True if the socket is full and no further events can be published until
+    // the application consumes some of the input.
+    bool inputPublisherBlocked;
+
+    // Queue of events that need to be published to the connection.
+    std::deque<DispatchEntry*> outboundQueue;
+
+    // Queue of events that have been published to the connection but that have not
+    // yet received a "finished" response from the application.
+    std::deque<DispatchEntry*> waitQueue;
+
+    explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
+
+    inline const std::string getInputChannelName() const { return inputChannel->getName(); }
+
+    const std::string getWindowName() const;
+    const char* getStatusLabel() const;
+
+    std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
new file mode 100644
index 0000000..640a69a
--- /dev/null
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Entry.h"
+
+#include "Connection.h"
+
+#include <android-base/stringprintf.h>
+#include <cutils/atomic.h>
+#include <inttypes.h>
+
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+static std::string motionActionToString(int32_t action) {
+    // Convert MotionEvent action to string
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+        case AMOTION_EVENT_ACTION_DOWN:
+            return "DOWN";
+        case AMOTION_EVENT_ACTION_MOVE:
+            return "MOVE";
+        case AMOTION_EVENT_ACTION_UP:
+            return "UP";
+        case AMOTION_EVENT_ACTION_POINTER_DOWN:
+            return "POINTER_DOWN";
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+            return "POINTER_UP";
+    }
+    return StringPrintf("%" PRId32, action);
+}
+
+static std::string keyActionToString(int32_t action) {
+    // Convert KeyEvent action to string
+    switch (action) {
+        case AKEY_EVENT_ACTION_DOWN:
+            return "DOWN";
+        case AKEY_EVENT_ACTION_UP:
+            return "UP";
+        case AKEY_EVENT_ACTION_MULTIPLE:
+            return "MULTIPLE";
+    }
+    return StringPrintf("%" PRId32, action);
+}
+
+// --- EventEntry ---
+
+EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
+      : sequenceNum(sequenceNum),
+        refCount(1),
+        type(type),
+        eventTime(eventTime),
+        policyFlags(policyFlags),
+        injectionState(nullptr),
+        dispatchInProgress(false) {}
+
+EventEntry::~EventEntry() {
+    releaseInjectionState();
+}
+
+void EventEntry::release() {
+    refCount -= 1;
+    if (refCount == 0) {
+        delete this;
+    } else {
+        ALOG_ASSERT(refCount > 0);
+    }
+}
+
+void EventEntry::releaseInjectionState() {
+    if (injectionState) {
+        injectionState->release();
+        injectionState = nullptr;
+    }
+}
+
+// --- ConfigurationChangedEntry ---
+
+ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
+      : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+
+ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
+
+void ConfigurationChangedEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+}
+
+// --- DeviceResetEntry ---
+
+DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
+      : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+
+DeviceResetEntry::~DeviceResetEntry() {}
+
+void DeviceResetEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
+}
+
+// --- KeyEntry ---
+
+KeyEntry::KeyEntry(uint32_t sequenceNum, 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, int32_t repeatCount,
+                   nsecs_t downTime)
+      : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        action(action),
+        flags(flags),
+        keyCode(keyCode),
+        scanCode(scanCode),
+        metaState(metaState),
+        repeatCount(repeatCount),
+        downTime(downTime),
+        syntheticRepeat(false),
+        interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+        interceptKeyWakeupTime(0) {}
+
+KeyEntry::~KeyEntry() {}
+
+void KeyEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+                        "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+                        "repeatCount=%d), policyFlags=0x%08x",
+                        deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+                        keyCode, scanCode, metaState, repeatCount, policyFlags);
+}
+
+void KeyEntry::recycle() {
+    releaseInjectionState();
+
+    dispatchInProgress = false;
+    syntheticRepeat = false;
+    interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+    interceptKeyWakeupTime = 0;
+}
+
+// --- MotionEntry ---
+
+MotionEntry::MotionEntry(uint32_t sequenceNum, 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, float xPrecision, float yPrecision,
+                         float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+                         uint32_t pointerCount, const PointerProperties* pointerProperties,
+                         const PointerCoords* pointerCoords, float xOffset, float yOffset)
+      : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
+        pointerCount(pointerCount) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+        if (xOffset || yOffset) {
+            this->pointerCoords[i].applyOffset(xOffset, yOffset);
+        }
+    }
+}
+
+MotionEntry::~MotionEntry() {}
+
+void MotionEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
+                        ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
+                        "buttonState=0x%08x, "
+                        "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
+                        "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
+                        deviceId, source, displayId, motionActionToString(action).c_str(),
+                        actionButton, flags, metaState, buttonState,
+                        motionClassificationToString(classification), edgeFlags, xPrecision,
+                        yPrecision, xCursorPosition, yCursorPosition);
+
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (i) {
+            msg += ", ";
+        }
+        msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(),
+                            pointerCoords[i].getY());
+    }
+    msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
+}
+
+// --- DispatchEntry ---
+
+volatile int32_t DispatchEntry::sNextSeqAtomic;
+
+DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
+                             float yOffset, float globalScaleFactor, float windowXScale,
+                             float windowYScale)
+      : seq(nextSeq()),
+        eventEntry(eventEntry),
+        targetFlags(targetFlags),
+        xOffset(xOffset),
+        yOffset(yOffset),
+        globalScaleFactor(globalScaleFactor),
+        windowXScale(windowXScale),
+        windowYScale(windowYScale),
+        deliveryTime(0),
+        resolvedAction(0),
+        resolvedFlags(0) {
+    eventEntry->refCount += 1;
+}
+
+DispatchEntry::~DispatchEntry() {
+    eventEntry->release();
+}
+
+uint32_t DispatchEntry::nextSeq() {
+    // Sequence number 0 is reserved and will never be returned.
+    uint32_t seq;
+    do {
+        seq = android_atomic_inc(&sNextSeqAtomic);
+    } while (!seq);
+    return seq;
+}
+
+// --- CommandEntry ---
+
+CommandEntry::CommandEntry(Command command)
+      : command(command),
+        eventTime(0),
+        keyEntry(nullptr),
+        userActivityEventType(0),
+        seq(0),
+        handled(false) {}
+
+CommandEntry::~CommandEntry() {}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
new file mode 100644
index 0000000..a9e22f1
--- /dev/null
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+
+#include "InjectionState.h"
+#include "InputTarget.h"
+
+#include <input/Input.h>
+#include <input/InputApplication.h>
+#include <stdint.h>
+#include <utils/Timers.h>
+#include <functional>
+#include <string>
+
+namespace android::inputdispatcher {
+
+struct EventEntry {
+    enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
+
+    uint32_t sequenceNum;
+    mutable int32_t refCount;
+    int32_t type;
+    nsecs_t eventTime;
+    uint32_t policyFlags;
+    InjectionState* injectionState;
+
+    bool dispatchInProgress; // initially false, set to true while dispatching
+
+    inline bool isInjected() const { return injectionState != nullptr; }
+
+    void release();
+
+    virtual void appendDescription(std::string& msg) const = 0;
+
+protected:
+    EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+    virtual ~EventEntry();
+    void releaseInjectionState();
+};
+
+struct ConfigurationChangedEntry : EventEntry {
+    explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
+    virtual void appendDescription(std::string& msg) const;
+
+protected:
+    virtual ~ConfigurationChangedEntry();
+};
+
+struct DeviceResetEntry : EventEntry {
+    int32_t deviceId;
+
+    DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
+    virtual void appendDescription(std::string& msg) const;
+
+protected:
+    virtual ~DeviceResetEntry();
+};
+
+struct KeyEntry : EventEntry {
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    int32_t action;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    int32_t repeatCount;
+    nsecs_t downTime;
+
+    bool syntheticRepeat; // set to true for synthetic key repeats
+
+    enum InterceptKeyResult {
+        INTERCEPT_KEY_RESULT_UNKNOWN,
+        INTERCEPT_KEY_RESULT_SKIP,
+        INTERCEPT_KEY_RESULT_CONTINUE,
+        INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
+    };
+    InterceptKeyResult interceptKeyResult; // set based on the interception result
+    nsecs_t interceptKeyWakeupTime;        // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+
+    KeyEntry(uint32_t sequenceNum, 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, int32_t repeatCount,
+             nsecs_t downTime);
+    virtual void appendDescription(std::string& msg) const;
+    void recycle();
+
+protected:
+    virtual ~KeyEntry();
+};
+
+struct MotionEntry : EventEntry {
+    nsecs_t eventTime;
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    int32_t action;
+    int32_t actionButton;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+    MotionClassification classification;
+    int32_t edgeFlags;
+    float xPrecision;
+    float yPrecision;
+    float xCursorPosition;
+    float yCursorPosition;
+    nsecs_t downTime;
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+
+    MotionEntry(uint32_t sequenceNum, 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, float xPrecision,
+                float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+                uint32_t pointerCount, const PointerProperties* pointerProperties,
+                const PointerCoords* pointerCoords, float xOffset, float yOffset);
+    virtual void appendDescription(std::string& msg) const;
+
+protected:
+    virtual ~MotionEntry();
+};
+
+// Tracks the progress of dispatching a particular event to a particular connection.
+struct DispatchEntry {
+    const uint32_t seq; // unique sequence number, never 0
+
+    EventEntry* eventEntry; // the event to dispatch
+    int32_t targetFlags;
+    float xOffset;
+    float yOffset;
+    float globalScaleFactor;
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+    nsecs_t deliveryTime; // time when the event was actually delivered
+
+    // Set to the resolved action and flags when the event is enqueued.
+    int32_t resolvedAction;
+    int32_t resolvedFlags;
+
+    DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
+                  float globalScaleFactor, float windowXScale, float windowYScale);
+    ~DispatchEntry();
+
+    inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
+
+    inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; }
+
+private:
+    static volatile int32_t sNextSeqAtomic;
+
+    static uint32_t nextSeq();
+};
+
+class InputDispatcher;
+// A command entry captures state and behavior for an action to be performed in the
+// dispatch loop after the initial processing has taken place.  It is essentially
+// a kind of continuation used to postpone sensitive policy interactions to a point
+// in the dispatch loop where it is safe to release the lock (generally after finishing
+// the critical parts of the dispatch cycle).
+//
+// The special thing about commands is that they can voluntarily release and reacquire
+// the dispatcher lock at will.  Initially when the command starts running, the
+// dispatcher lock is held.  However, if the command needs to call into the policy to
+// do some work, it can release the lock, do the work, then reacquire the lock again
+// before returning.
+//
+// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+// never calls into the policy while holding its lock.
+//
+// Commands are implicitly 'LockedInterruptible'.
+struct CommandEntry;
+typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
+
+class Connection;
+struct CommandEntry {
+    explicit CommandEntry(Command command);
+    ~CommandEntry();
+
+    Command command;
+
+    // parameters for the command (usage varies by command)
+    sp<Connection> connection;
+    nsecs_t eventTime;
+    KeyEntry* keyEntry;
+    sp<InputApplicationHandle> inputApplicationHandle;
+    std::string reason;
+    int32_t userActivityEventType;
+    uint32_t seq;
+    bool handled;
+    sp<InputChannel> inputChannel;
+    sp<IBinder> oldToken;
+    sp<IBinder> newToken;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
new file mode 100644
index 0000000..b2d0a26
--- /dev/null
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InjectionState.h"
+
+#include <log/log.h>
+
+namespace android::inputdispatcher {
+
+InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid)
+      : refCount(1),
+        injectorPid(injectorPid),
+        injectorUid(injectorUid),
+        injectionResult(INPUT_EVENT_INJECTION_PENDING),
+        injectionIsAsync(false),
+        pendingForegroundDispatches(0) {}
+
+InjectionState::~InjectionState() {}
+
+void InjectionState::release() {
+    refCount -= 1;
+    if (refCount == 0) {
+        delete this;
+    } else {
+        ALOG_ASSERT(refCount > 0);
+    }
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
new file mode 100644
index 0000000..311a0f1
--- /dev/null
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+
+#include "InputDispatcherInterface.h"
+
+#include <stdint.h>
+
+namespace android::inputdispatcher {
+
+/*
+ * Constants used to determine the input event injection synchronization mode.
+ */
+enum {
+    /* Injection is asynchronous and is assumed always to be successful. */
+    INPUT_EVENT_INJECTION_SYNC_NONE = 0,
+
+    /* Waits for previous events to be dispatched so that the input dispatcher can determine
+     * whether input event injection willbe permitted based on the current input focus.
+     * Does not wait for the input event to finish processing. */
+    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
+
+    /* Waits for the input event to be completely processed. */
+    INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
+};
+
+struct InjectionState {
+    mutable int32_t refCount;
+
+    int32_t injectorPid;
+    int32_t injectorUid;
+    int32_t injectionResult;             // initially INPUT_EVENT_INJECTION_PENDING
+    bool injectionIsAsync;               // set to true if injection is not waiting for the result
+    int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+
+    InjectionState(int32_t injectorPid, int32_t injectorUid);
+    void release();
+
+private:
+    ~InjectionState();
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
similarity index 61%
rename from services/inputflinger/InputDispatcher.cpp
rename to services/inputflinger/dispatcher/InputDispatcher.cpp
index 1eb979e..039462e 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -45,6 +45,8 @@
 
 #include "InputDispatcher.h"
 
+#include "Connection.h"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -56,10 +58,10 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-#include <powermanager/PowerManager.h>
 #include <binder/Binder.h>
+#include <log/log.h>
+#include <powermanager/PowerManager.h>
+#include <utils/Trace.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -68,7 +70,7 @@
 
 using android::base::StringPrintf;
 
-namespace android {
+namespace android::inputdispatcher {
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
@@ -98,10 +100,6 @@
 // Number of recent events to keep for debugging purposes.
 constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
 
-// Sequence number for synthesized or injected events.
-constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
-
-
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
@@ -110,71 +108,23 @@
     return value ? "true" : "false";
 }
 
-static std::string motionActionToString(int32_t action) {
-    // Convert MotionEvent action to string
-    switch(action & AMOTION_EVENT_ACTION_MASK) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AMOTION_EVENT_ACTION_MOVE:
-            return "MOVE";
-        case AMOTION_EVENT_ACTION_UP:
-            return "UP";
-        case AMOTION_EVENT_ACTION_POINTER_DOWN:
-            return "POINTER_DOWN";
-        case AMOTION_EVENT_ACTION_POINTER_UP:
-            return "POINTER_UP";
-    }
-    return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
-    // Convert KeyEvent action to string
-    switch (action) {
-        case AKEY_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AKEY_EVENT_ACTION_UP:
-            return "UP";
-        case AKEY_EVENT_ACTION_MULTIPLE:
-            return "MULTIPLE";
-    }
-    return StringPrintf("%" PRId32, action);
-}
-
-static std::string dispatchModeToString(int32_t dispatchMode) {
-    switch (dispatchMode) {
-        case InputTarget::FLAG_DISPATCH_AS_IS:
-            return "DISPATCH_AS_IS";
-        case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
-            return "DISPATCH_AS_OUTSIDE";
-        case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
-            return "DISPATCH_AS_HOVER_ENTER";
-        case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
-            return "DISPATCH_AS_HOVER_EXIT";
-        case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
-            return "DISPATCH_AS_SLIPPERY_EXIT";
-        case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
-            return "DISPATCH_AS_SLIPPERY_ENTER";
-    }
-    return StringPrintf("%" PRId32, dispatchMode);
-}
-
 static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
-    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
-            >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+            AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
 }
 
 static bool isValidKeyAction(int32_t action) {
     switch (action) {
-    case AKEY_EVENT_ACTION_DOWN:
-    case AKEY_EVENT_ACTION_UP:
-        return true;
-    default:
-        return false;
+        case AKEY_EVENT_ACTION_DOWN:
+        case AKEY_EVENT_ACTION_UP:
+            return true;
+        default:
+            return false;
     }
 }
 
 static bool validateKeyEvent(int32_t action) {
-    if (! isValidKeyAction(action)) {
+    if (!isValidKeyAction(action)) {
         ALOGE("Key event has invalid action code 0x%x", action);
         return false;
     }
@@ -183,46 +133,46 @@
 
 static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
     switch (action & AMOTION_EVENT_ACTION_MASK) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_UP:
-    case AMOTION_EVENT_ACTION_CANCEL:
-    case AMOTION_EVENT_ACTION_MOVE:
-    case AMOTION_EVENT_ACTION_OUTSIDE:
-    case AMOTION_EVENT_ACTION_HOVER_ENTER:
-    case AMOTION_EVENT_ACTION_HOVER_MOVE:
-    case AMOTION_EVENT_ACTION_HOVER_EXIT:
-    case AMOTION_EVENT_ACTION_SCROLL:
-        return true;
-    case AMOTION_EVENT_ACTION_POINTER_DOWN:
-    case AMOTION_EVENT_ACTION_POINTER_UP: {
-        int32_t index = getMotionEventActionPointerIndex(action);
-        return index >= 0 && index < pointerCount;
-    }
-    case AMOTION_EVENT_ACTION_BUTTON_PRESS:
-    case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
-        return actionButton != 0;
-    default:
-        return false;
+        case AMOTION_EVENT_ACTION_DOWN:
+        case AMOTION_EVENT_ACTION_UP:
+        case AMOTION_EVENT_ACTION_CANCEL:
+        case AMOTION_EVENT_ACTION_MOVE:
+        case AMOTION_EVENT_ACTION_OUTSIDE:
+        case AMOTION_EVENT_ACTION_HOVER_ENTER:
+        case AMOTION_EVENT_ACTION_HOVER_MOVE:
+        case AMOTION_EVENT_ACTION_HOVER_EXIT:
+        case AMOTION_EVENT_ACTION_SCROLL:
+            return true;
+        case AMOTION_EVENT_ACTION_POINTER_DOWN:
+        case AMOTION_EVENT_ACTION_POINTER_UP: {
+            int32_t index = getMotionEventActionPointerIndex(action);
+            return index >= 0 && index < pointerCount;
+        }
+        case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+        case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
+            return actionButton != 0;
+        default:
+            return false;
     }
 }
 
 static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
-        const PointerProperties* pointerProperties) {
-    if (! isValidMotionAction(action, actionButton, pointerCount)) {
+                                const PointerProperties* pointerProperties) {
+    if (!isValidMotionAction(action, actionButton, pointerCount)) {
         ALOGE("Motion event has invalid action code 0x%x", action);
         return false;
     }
     if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
         ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.",
-                pointerCount, MAX_POINTERS);
+              pointerCount, MAX_POINTERS);
         return false;
     }
     BitSet32 pointerIdBits;
     for (size_t i = 0; i < pointerCount; i++) {
         int32_t id = pointerProperties[i].id;
         if (id < 0 || id > MAX_POINTER_ID) {
-            ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
-                    id, MAX_POINTER_ID);
+            ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id,
+                  MAX_POINTER_ID);
             return false;
         }
         if (pointerIdBits.hasBit(id)) {
@@ -262,12 +212,32 @@
  * if the entry is not found.
  * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
  */
-template <typename T, typename U>
-static T getValueByKey(const std::unordered_map<U, T>& map, U key) {
+template <typename K, typename V>
+static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
     auto it = map.find(key);
-    return it != map.end() ? it->second : T{};
+    return it != map.end() ? it->second : V{};
 }
 
+/**
+ * Find the entry in std::unordered_map by value, and remove it.
+ * If more than one entry has the same value, then all matching
+ * key-value pairs will be removed.
+ *
+ * Return true if at least one value has been removed.
+ */
+template <typename K, typename V>
+static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
+    bool removed = false;
+    for (auto it = map.begin(); it != map.end();) {
+        if (it->second == value) {
+            it = map.erase(it);
+            removed = true;
+        } else {
+            it++;
+        }
+    }
+    return removed;
+}
 
 // --- InputDispatcher ---
 
@@ -300,8 +270,9 @@
         drainInboundQueueLocked();
     }
 
-    while (mConnectionsByFd.size() != 0) {
-        unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
+    while (!mConnectionsByFd.empty()) {
+        sp<Connection> connection = mConnectionsByFd.begin()->second;
+        unregisterInputChannel(connection->inputChannel);
     }
 }
 
@@ -358,7 +329,7 @@
 
     // Ready to start a new event.
     // If we don't already have a pending event, go grab one.
-    if (! mPendingEvent) {
+    if (!mPendingEvent) {
         if (mInboundQueue.empty()) {
             if (isAppSwitchDue) {
                 // The inbound queue is empty so the app switch key we were waiting
@@ -414,63 +385,59 @@
     }
 
     switch (mPendingEvent->type) {
-    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
-        ConfigurationChangedEntry* typedEntry =
-                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
-        done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
-        dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
-        break;
-    }
+        case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+            ConfigurationChangedEntry* typedEntry =
+                    static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+            done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+            dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
+            break;
+        }
 
-    case EventEntry::TYPE_DEVICE_RESET: {
-        DeviceResetEntry* typedEntry =
-                static_cast<DeviceResetEntry*>(mPendingEvent);
-        done = dispatchDeviceResetLocked(currentTime, typedEntry);
-        dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
-        break;
-    }
+        case EventEntry::TYPE_DEVICE_RESET: {
+            DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
+            done = dispatchDeviceResetLocked(currentTime, typedEntry);
+            dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
+            break;
+        }
 
-    case EventEntry::TYPE_KEY: {
-        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
-        if (isAppSwitchDue) {
-            if (isAppSwitchKeyEvent(typedEntry)) {
-                resetPendingAppSwitchLocked(true);
-                isAppSwitchDue = false;
-            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
+        case EventEntry::TYPE_KEY: {
+            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+            if (isAppSwitchDue) {
+                if (isAppSwitchKeyEvent(typedEntry)) {
+                    resetPendingAppSwitchLocked(true);
+                    isAppSwitchDue = false;
+                } else if (dropReason == DROP_REASON_NOT_DROPPED) {
+                    dropReason = DROP_REASON_APP_SWITCH;
+                }
+            }
+            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+                dropReason = DROP_REASON_STALE;
+            }
+            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DROP_REASON_BLOCKED;
+            }
+            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+            break;
+        }
+
+        case EventEntry::TYPE_MOTION: {
+            MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+            if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
                 dropReason = DROP_REASON_APP_SWITCH;
             }
+            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
+                dropReason = DROP_REASON_STALE;
+            }
+            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DROP_REASON_BLOCKED;
+            }
+            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+            break;
         }
-        if (dropReason == DROP_REASON_NOT_DROPPED
-                && isStaleEvent(currentTime, typedEntry)) {
-            dropReason = DROP_REASON_STALE;
-        }
-        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-            dropReason = DROP_REASON_BLOCKED;
-        }
-        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
-        break;
-    }
 
-    case EventEntry::TYPE_MOTION: {
-        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
-        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
-            dropReason = DROP_REASON_APP_SWITCH;
-        }
-        if (dropReason == DROP_REASON_NOT_DROPPED
-                && isStaleEvent(currentTime, typedEntry)) {
-            dropReason = DROP_REASON_STALE;
-        }
-        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-            dropReason = DROP_REASON_BLOCKED;
-        }
-        done = dispatchMotionLocked(currentTime, typedEntry,
-                &dropReason, nextWakeupTime);
-        break;
-    }
-
-    default:
-        ALOG_ASSERT(false);
-        break;
+        default:
+            ALOG_ASSERT(false);
+            break;
     }
 
     if (done) {
@@ -480,7 +447,7 @@
         mLastDropReason = dropReason;
 
         releasePendingEventLocked();
-        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
+        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
     }
 }
 
@@ -490,55 +457,56 @@
     traceInboundQueueLengthLocked();
 
     switch (entry->type) {
-    case EventEntry::TYPE_KEY: {
-        // Optimize app switch latency.
-        // If the application takes too long to catch up then we drop all events preceding
-        // the app switch key.
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
-        if (isAppSwitchKeyEvent(keyEntry)) {
-            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
-                mAppSwitchSawKeyDown = true;
-            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
-                if (mAppSwitchSawKeyDown) {
+        case EventEntry::TYPE_KEY: {
+            // Optimize app switch latency.
+            // If the application takes too long to catch up then we drop all events preceding
+            // the app switch key.
+            KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+            if (isAppSwitchKeyEvent(keyEntry)) {
+                if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+                    mAppSwitchSawKeyDown = true;
+                } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+                    if (mAppSwitchSawKeyDown) {
 #if DEBUG_APP_SWITCH
-                    ALOGD("App switch is pending!");
+                        ALOGD("App switch is pending!");
 #endif
-                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
-                    mAppSwitchSawKeyDown = false;
+                        mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+                        mAppSwitchSawKeyDown = false;
+                        needWake = true;
+                    }
+                }
+            }
+            break;
+        }
+
+        case EventEntry::TYPE_MOTION: {
+            // Optimize case where the current application is unresponsive and the user
+            // decides to touch a window in a different application.
+            // If the application takes too long to catch up then we drop all events preceding
+            // the touch into the other window.
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+            if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
+                (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+                mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
+                mInputTargetWaitApplicationToken != nullptr) {
+                int32_t displayId = motionEntry->displayId;
+                int32_t x =
+                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+                int32_t y =
+                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+                sp<InputWindowHandle> touchedWindowHandle =
+                        findTouchedWindowAtLocked(displayId, x, y);
+                if (touchedWindowHandle != nullptr &&
+                    touchedWindowHandle->getApplicationToken() !=
+                            mInputTargetWaitApplicationToken) {
+                    // User touched a different application than the one we are waiting on.
+                    // Flag the event, and start pruning the input queue.
+                    mNextUnblockedEvent = motionEntry;
                     needWake = true;
                 }
             }
+            break;
         }
-        break;
-    }
-
-    case EventEntry::TYPE_MOTION: {
-        // Optimize case where the current application is unresponsive and the user
-        // decides to touch a window in a different application.
-        // If the application takes too long to catch up then we drop all events preceding
-        // the touch into the other window.
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
-                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
-                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
-                && mInputTargetWaitApplicationToken != nullptr) {
-            int32_t displayId = motionEntry->displayId;
-            int32_t x = int32_t(motionEntry->pointerCoords[0].
-                    getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(motionEntry->pointerCoords[0].
-                    getAxisValue(AMOTION_EVENT_AXIS_Y));
-            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
-            if (touchedWindowHandle != nullptr
-                    && touchedWindowHandle->getApplicationToken()
-                            != mInputTargetWaitApplicationToken) {
-                // User touched a different application than the one we are waiting on.
-                // Flag the event, and start pruning the input queue.
-                mNextUnblockedEvent = motionEntry;
-                needWake = true;
-            }
-        }
-        break;
-    }
     }
 
     return needWake;
@@ -553,8 +521,9 @@
     }
 }
 
-sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
-        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
+sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
+                                                                 int32_t y, bool addOutsideTargets,
+                                                                 bool addPortalWindows) {
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -564,18 +533,19 @@
 
             if (windowInfo->visible) {
                 if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
-                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    bool isTouchModal = (flags &
+                                         (InputWindowInfo::FLAG_NOT_FOCUSABLE |
+                                          InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                         int32_t portalToDisplayId = windowInfo->portalToDisplayId;
-                        if (portalToDisplayId != ADISPLAY_ID_NONE
-                                && portalToDisplayId != displayId) {
+                        if (portalToDisplayId != ADISPLAY_ID_NONE &&
+                            portalToDisplayId != displayId) {
                             if (addPortalWindows) {
                                 // For the monitoring channels of the display.
                                 mTempTouchState.addPortalWindow(windowHandle);
                             }
-                            return findTouchedWindowAtLocked(
-                                    portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+                            return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+                                                             addOutsideTargets, addPortalWindows);
                         }
                         // Found window.
                         return windowHandle;
@@ -583,8 +553,9 @@
                 }
 
                 if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(
-                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+                    mTempTouchState.addOrUpdateWindow(windowHandle,
+                                                      InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+                                                      BitSet32(0));
                 }
             }
         }
@@ -592,7 +563,7 @@
     return nullptr;
 }
 
-std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
+std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
         int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
     std::vector<TouchedMonitor> touchedMonitors;
 
@@ -601,14 +572,15 @@
     for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
         const InputWindowInfo* windowInfo = portalWindow->getInfo();
         monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
-        addGestureMonitors(monitors, touchedMonitors,
-                -windowInfo->frameLeft, -windowInfo->frameTop);
+        addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
+                           -windowInfo->frameTop);
     }
     return touchedMonitors;
 }
 
 void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
-        std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset, float yOffset) {
+                                         std::vector<TouchedMonitor>& outTouchedMonitors,
+                                         float xOffset, float yOffset) {
     if (monitors.empty()) {
         return;
     }
@@ -621,68 +593,66 @@
 void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
     const char* reason;
     switch (dropReason) {
-    case DROP_REASON_POLICY:
+        case DROP_REASON_POLICY:
 #if DEBUG_INBOUND_EVENT_DETAILS
-        ALOGD("Dropped event because policy consumed it.");
+            ALOGD("Dropped event because policy consumed it.");
 #endif
-        reason = "inbound event was dropped because the policy consumed it";
-        break;
-    case DROP_REASON_DISABLED:
-        if (mLastDropReason != DROP_REASON_DISABLED) {
-            ALOGI("Dropped event because input dispatch is disabled.");
-        }
-        reason = "inbound event was dropped because input dispatch is disabled";
-        break;
-    case DROP_REASON_APP_SWITCH:
-        ALOGI("Dropped event because of pending overdue app switch.");
-        reason = "inbound event was dropped because of pending overdue app switch";
-        break;
-    case DROP_REASON_BLOCKED:
-        ALOGI("Dropped event because the current application is not responding and the user "
-                "has started interacting with a different application.");
-        reason = "inbound event was dropped because the current application is not responding "
-                "and the user has started interacting with a different application";
-        break;
-    case DROP_REASON_STALE:
-        ALOGI("Dropped event because it is stale.");
-        reason = "inbound event was dropped because it is stale";
-        break;
-    default:
-        ALOG_ASSERT(false);
-        return;
+            reason = "inbound event was dropped because the policy consumed it";
+            break;
+        case DROP_REASON_DISABLED:
+            if (mLastDropReason != DROP_REASON_DISABLED) {
+                ALOGI("Dropped event because input dispatch is disabled.");
+            }
+            reason = "inbound event was dropped because input dispatch is disabled";
+            break;
+        case DROP_REASON_APP_SWITCH:
+            ALOGI("Dropped event because of pending overdue app switch.");
+            reason = "inbound event was dropped because of pending overdue app switch";
+            break;
+        case DROP_REASON_BLOCKED:
+            ALOGI("Dropped event because the current application is not responding and the user "
+                  "has started interacting with a different application.");
+            reason = "inbound event was dropped because the current application is not responding "
+                     "and the user has started interacting with a different application";
+            break;
+        case DROP_REASON_STALE:
+            ALOGI("Dropped event because it is stale.");
+            reason = "inbound event was dropped because it is stale";
+            break;
+        default:
+            ALOG_ASSERT(false);
+            return;
     }
 
     switch (entry->type) {
-    case EventEntry::TYPE_KEY: {
-        CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
-        synthesizeCancelationEventsForAllConnectionsLocked(options);
-        break;
-    }
-    case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-        if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
-            CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
-            synthesizeCancelationEventsForAllConnectionsLocked(options);
-        } else {
+        case EventEntry::TYPE_KEY: {
             CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
             synthesizeCancelationEventsForAllConnectionsLocked(options);
+            break;
         }
-        break;
-    }
+        case EventEntry::TYPE_MOTION: {
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+            if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+                CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+                synthesizeCancelationEventsForAllConnectionsLocked(options);
+            } else {
+                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+                synthesizeCancelationEventsForAllConnectionsLocked(options);
+            }
+            break;
+        }
     }
 }
 
 static bool isAppSwitchKeyCode(int32_t keyCode) {
-    return keyCode == AKEYCODE_HOME
-            || keyCode == AKEYCODE_ENDCALL
-            || keyCode == AKEYCODE_APP_SWITCH;
+    return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL ||
+            keyCode == AKEYCODE_APP_SWITCH;
 }
 
 bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
-    return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
-            && isAppSwitchKeyCode(keyEntry->keyCode)
-            && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
-            && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+    return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) &&
+            (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) &&
+            (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
 }
 
 bool InputDispatcher::isAppSwitchPendingLocked() {
@@ -768,7 +738,7 @@
     }
 }
 
-InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
+KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
     // Reuse the repeated key entry if it is otherwise unreferenced.
@@ -780,10 +750,11 @@
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
     } else {
-        KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
-                entry->deviceId, entry->source, entry->displayId, policyFlags,
-                entry->action, entry->flags, entry->keyCode, entry->scanCode,
-                entry->metaState, entry->repeatCount + 1, entry->downTime);
+        KeyEntry* newEntry =
+                new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId,
+                             entry->source, entry->displayId, policyFlags, entry->action,
+                             entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+                             entry->repeatCount + 1, entry->downTime);
 
         mKeyRepeatState.lastKeyEntry = newEntry;
         entry->release();
@@ -800,8 +771,8 @@
     return entry;
 }
 
-bool InputDispatcher::dispatchConfigurationChangedLocked(
-        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
+                                                         ConfigurationChangedEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
 #endif
@@ -817,30 +788,27 @@
     return true;
 }
 
-bool InputDispatcher::dispatchDeviceResetLocked(
-        nsecs_t currentTime, DeviceResetEntry* entry) {
+bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
-            entry->deviceId);
+          entry->deviceId);
 #endif
 
-    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
-            "device was reset");
+    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
     options.deviceId = entry->deviceId;
     synthesizeCancelationEventsForAllConnectionsLocked(options);
     return true;
 }
 
 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
-        DropReason* dropReason, nsecs_t* nextWakeupTime) {
+                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
-    if (! entry->dispatchInProgress) {
-        if (entry->repeatCount == 0
-                && entry->action == AKEY_EVENT_ACTION_DOWN
-                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
-                && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
-            if (mKeyRepeatState.lastKeyEntry
-                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+    if (!entry->dispatchInProgress) {
+        if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
+            (entry->policyFlags & POLICY_FLAG_TRUSTED) &&
+            (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
+            if (mKeyRepeatState.lastKeyEntry &&
+                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                 // We have seen two identical key downs in a row which indicates that the device
                 // driver is automatically generating key repeats itself.  We take note of the
                 // repeat here, but we disable our own next key repeat timer since it is clear that
@@ -855,7 +823,7 @@
             }
             mKeyRepeatState.lastKeyEntry = entry;
             entry->refCount += 1;
-        } else if (! entry->syntheticRepeat) {
+        } else if (!entry->syntheticRepeat) {
             resetKeyRepeatLocked();
         }
 
@@ -890,8 +858,7 @@
             sp<InputWindowHandle> focusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
             if (focusedWindowHandle != nullptr) {
-                commandEntry->inputChannel =
-                    getInputChannelLocked(focusedWindowHandle->getToken());
+                commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
             }
             commandEntry->keyEntry = entry;
             postCommandLocked(std::move(commandEntry));
@@ -908,16 +875,17 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
-                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(entry,
+                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                                                             : INPUT_EVENT_INJECTION_FAILED);
         mReporter->reportDroppedKey(entry->sequenceNum);
         return true;
     }
 
     // Identify targets.
     std::vector<InputTarget> inputTargets;
-    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
-            entry, inputTargets, nextWakeupTime);
+    int32_t injectionResult =
+            findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
     }
@@ -938,20 +906,19 @@
 void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
-            "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
-            "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
-            prefix,
-            entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
-            entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->repeatCount, entry->downTime);
+          "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+          "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+          prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
+          entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode,
+          entry->metaState, entry->repeatCount, entry->downTime);
 #endif
 }
 
-bool InputDispatcher::dispatchMotionLocked(
-        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
+                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
     ATRACE_CALL();
     // Preprocessing.
-    if (! entry->dispatchInProgress) {
+    if (!entry->dispatchInProgress) {
         entry->dispatchInProgress = true;
 
         logOutboundMotionDetails("dispatchMotion - ", entry);
@@ -959,8 +926,9 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
-                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(entry,
+                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                                                             : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
 
@@ -973,12 +941,13 @@
     int32_t injectionResult;
     if (isPointerEvent) {
         // Pointer event.  (eg. touchscreen)
-        injectionResult = findTouchedWindowTargetsLocked(currentTime,
-                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
+        injectionResult =
+                findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime,
+                                               &conflictingPointerActions);
     } else {
         // Non touch event.  (eg. trackball)
-        injectionResult = findFocusedWindowTargetsLocked(currentTime,
-                entry, inputTargets, nextWakeupTime);
+        injectionResult =
+                findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
     }
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
@@ -987,9 +956,9 @@
     setInjectionResult(entry, injectionResult);
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
         if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
-            CancelationOptions::Mode mode(isPointerEvent ?
-                    CancelationOptions::CANCEL_POINTER_EVENTS :
-                    CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+            CancelationOptions::Mode mode(isPointerEvent
+                                                  ? CancelationOptions::CANCEL_POINTER_EVENTS
+                                                  : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
             CancelationOptions options(mode, "input event injection failed");
             synthesizeCancelationEventsForMonitorsLocked(options);
         }
@@ -1009,7 +978,7 @@
                 for (size_t i = 0; i < state.portalWindows.size(); i++) {
                     const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo();
                     addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
-                            -windowInfo->frameLeft, -windowInfo->frameTop);
+                                                     -windowInfo->frameLeft, -windowInfo->frameTop);
                 }
             }
         }
@@ -1018,50 +987,46 @@
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                "conflicting pointer actions");
+                                   "conflicting pointer actions");
         synthesizeCancelationEventsForAllConnectionsLocked(options);
     }
     dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
-
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-            ", policyFlags=0x%x, "
-            "action=0x%x, actionButton=0x%x, flags=0x%x, "
-            "metaState=0x%x, buttonState=0x%x,"
-            "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-            prefix,
-            entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
-            entry->action, entry->actionButton, entry->flags,
-            entry->metaState, entry->buttonState,
-            entry->edgeFlags, entry->xPrecision, entry->yPrecision,
-            entry->downTime);
+          ", policyFlags=0x%x, "
+          "action=0x%x, actionButton=0x%x, flags=0x%x, "
+          "metaState=0x%x, buttonState=0x%x,"
+          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+          prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
+          entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState,
+          entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
+          entry->downTime);
 
     for (uint32_t i = 0; i < entry->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
-                "x=%f, y=%f, pressure=%f, size=%f, "
-                "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-                "orientation=%f",
-                i, entry->pointerProperties[i].id,
-                entry->pointerProperties[i].toolType,
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+              "x=%f, y=%f, pressure=%f, size=%f, "
+              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+              "orientation=%f",
+              i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType,
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
 }
 
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
-        EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
+                                          const std::vector<InputTarget>& inputTargets) {
     ATRACE_CALL();
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("dispatchEventToCurrentInputTargets");
@@ -1072,25 +1037,23 @@
     pokeUserActivityLocked(eventEntry);
 
     for (const InputTarget& inputTarget : inputTargets) {
-        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
-        if (connectionIndex >= 0) {
-            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
+        sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel);
+        if (connection != nullptr) {
             prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
         } else {
 #if DEBUG_FOCUS
             ALOGD("Dropping event delivery to target with channel '%s' because it "
-                    "is no longer registered with the input dispatcher.",
-                    inputTarget.inputChannel->getName().c_str());
+                  "is no longer registered with the input dispatcher.",
+                  inputTarget.inputChannel->getName().c_str());
 #endif
         }
     }
 }
 
-int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
-        const EventEntry* entry,
+int32_t InputDispatcher::handleTargetsNotReadyLocked(
+        nsecs_t currentTime, const EventEntry* entry,
         const sp<InputApplicationHandle>& applicationHandle,
-        const sp<InputWindowHandle>& windowHandle,
-        nsecs_t* nextWakeupTime, const char* reason) {
+        const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
     if (applicationHandle == nullptr && windowHandle == nullptr) {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
 #if DEBUG_FOCUS
@@ -1106,15 +1069,14 @@
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
 #if DEBUG_FOCUS
             ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
-                    getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
-                    reason);
+                  getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
 #endif
             nsecs_t timeout;
             if (windowHandle != nullptr) {
                 timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             } else if (applicationHandle != nullptr) {
-                timeout = applicationHandle->getDispatchingTimeout(
-                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+                timeout =
+                        applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             } else {
                 timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
             }
@@ -1139,8 +1101,8 @@
     }
 
     if (currentTime >= mInputTargetWaitTimeoutTime) {
-        onANRLocked(currentTime, applicationHandle, windowHandle,
-                entry->eventTime, mInputTargetWaitStartTime, reason);
+        onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime,
+                    mInputTargetWaitStartTime, reason);
 
         // Force poll loop to wake up immediately on next iteration once we get the
         // ANR response back from the policy.
@@ -1162,8 +1124,8 @@
     }
 }
 
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
-        const sp<InputChannel>& inputChannel) {
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
+        nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {
     if (newTimeout > 0) {
         // Extend the timeout.
         mInputTargetWaitTimeoutTime = now() + newTimeout;
@@ -1172,28 +1134,24 @@
         mInputTargetWaitTimeoutExpired = true;
 
         // Input state will not be realistic.  Mark it out of sync.
-        if (inputChannel.get()) {
-            ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-            if (connectionIndex >= 0) {
-                sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-                sp<IBinder> token = connection->inputChannel->getToken();
+        sp<Connection> connection = getConnectionLocked(inputChannel);
+        if (connection != nullptr) {
+            sp<IBinder> token = connection->inputChannel->getToken();
 
-                if (token != nullptr) {
-                    removeWindowByTokenLocked(token);
-                }
+            if (token != nullptr) {
+                removeWindowByTokenLocked(token);
+            }
 
-                if (connection->status == Connection::STATUS_NORMAL) {
-                    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
-                            "application not responding");
-                    synthesizeCancelationEventsForConnectionLocked(connection, options);
-                }
+            if (connection->status == Connection::STATUS_NORMAL) {
+                CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+                                           "application not responding");
+                synthesizeCancelationEventsForConnectionLocked(connection, options);
             }
         }
     }
 }
 
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
-        nsecs_t currentTime) {
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
     if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
         return currentTime - mInputTargetWaitStartTime;
     }
@@ -1202,7 +1160,7 @@
 
 void InputDispatcher::resetANRTimeoutsLocked() {
 #if DEBUG_FOCUS
-        ALOGD("Resetting ANR timeouts.");
+    ALOGD("Resetting ANR timeouts.");
 #endif
 
     // Reset input target wait timeout.
@@ -1218,26 +1176,28 @@
 int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
     int32_t displayId;
     switch (entry->type) {
-    case EventEntry::TYPE_KEY: {
-        const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
-        displayId = typedEntry->displayId;
-        break;
-    }
-    case EventEntry::TYPE_MOTION: {
-        const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
-        displayId = typedEntry->displayId;
-        break;
-    }
-    default: {
-        ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
-        return ADISPLAY_ID_NONE;
-    }
+        case EventEntry::TYPE_KEY: {
+            const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
+            displayId = typedEntry->displayId;
+            break;
+        }
+        case EventEntry::TYPE_MOTION: {
+            const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
+            displayId = typedEntry->displayId;
+            break;
+        }
+        default: {
+            ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+            return ADISPLAY_ID_NONE;
+        }
     }
     return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
 }
 
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
-        const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
+                                                        const EventEntry* entry,
+                                                        std::vector<InputTarget>& inputTargets,
+                                                        nsecs_t* nextWakeupTime) {
     int32_t injectionResult;
     std::string reason;
 
@@ -1251,16 +1211,18 @@
     // then drop the event.
     if (focusedWindowHandle == nullptr) {
         if (focusedApplicationHandle != nullptr) {
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    focusedApplicationHandle, nullptr, nextWakeupTime,
-                    "Waiting because no window has focus but there is a "
-                    "focused application that may eventually add a window "
-                    "when it finishes starting up.");
+            injectionResult =
+                    handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+                                                nullptr, nextWakeupTime,
+                                                "Waiting because no window has focus but there is "
+                                                "a focused application that may eventually add a "
+                                                "window when it finishes starting up.");
             goto Unresponsive;
         }
 
         ALOGI("Dropping event because there is no focused window or focused application in display "
-                "%" PRId32 ".", displayId);
+              "%" PRId32 ".",
+              displayId);
         injectionResult = INPUT_EVENT_INJECTION_FAILED;
         goto Failed;
     }
@@ -1272,19 +1234,19 @@
     }
 
     // Check whether the window is ready for more input.
-    reason = checkWindowReadyForMoreInputLocked(currentTime,
-            focusedWindowHandle, entry, "focused");
+    reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
     if (!reason.empty()) {
-        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str());
+        injectionResult =
+                handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+                                            focusedWindowHandle, nextWakeupTime, reason.c_str());
         goto Unresponsive;
     }
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
     addWindowTargetLocked(focusedWindowHandle,
-            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
-            inputTargets);
+                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+                          BitSet32(0), inputTargets);
 
     // Done.
 Failed:
@@ -1293,15 +1255,17 @@
     updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
     ALOGD("findFocusedWindow finished: injectionResult=%d, "
-            "timeSpentWaitingForApplication=%0.1fms",
-            injectionResult, timeSpentWaitingForApplication / 1000000.0);
+          "timeSpentWaitingForApplication=%0.1fms",
+          injectionResult, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
-        const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
-        bool* outConflictingPointerActions) {
+                                                        const MotionEntry* entry,
+                                                        std::vector<InputTarget>& inputTargets,
+                                                        nsecs_t* nextWakeupTime,
+                                                        bool* outConflictingPointerActions) {
     ATRACE_CALL();
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
@@ -1331,16 +1295,14 @@
     }
 
     bool isSplit = mTempTouchState.split;
-    bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0
-            && (mTempTouchState.deviceId != entry->deviceId
-                    || mTempTouchState.source != entry->source
-                    || mTempTouchState.displayId != displayId);
-    bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
-            || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
-            || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
-    bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
-            || maskedAction == AMOTION_EVENT_ACTION_SCROLL
-            || isHoverAction);
+    bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
+            (mTempTouchState.deviceId != entry->deviceId ||
+             mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId);
+    bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
+                          maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+                          maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
+    bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+                       maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
     const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE;
     bool wrongDevice = false;
     if (newGesture) {
@@ -1348,7 +1310,8 @@
         if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
 #if DEBUG_FOCUS
             ALOGD("Dropping event because a pointer for a different device is already down "
-                    "in display %" PRId32, displayId);
+                  "in display %" PRId32,
+                  displayId);
 #endif
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
@@ -1365,7 +1328,8 @@
     } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
 #if DEBUG_FOCUS
         ALOGI("Dropping move event because a pointer for a different device is already active "
-                "in display %" PRId32, displayId);
+              "in display %" PRId32,
+              displayId);
 #endif
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
@@ -1389,16 +1353,17 @@
             y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
-                displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+        sp<InputWindowHandle> newTouchedWindowHandle =
+                findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
+                                          true /*addPortalWindows*/);
 
         std::vector<TouchedMonitor> newGestureMonitors = isDown
                 ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
                 : std::vector<TouchedMonitor>{};
 
         // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindowHandle != nullptr
-                && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
+        if (newTouchedWindowHandle != nullptr &&
+            newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
             // New window supports splitting, but we should never split mouse events.
             isSplit = !isFromMouse;
         } else if (isSplit) {
@@ -1415,7 +1380,8 @@
 
         if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
             ALOGI("Dropping event because there is no touchable window or gesture monitor at "
-                    "(%d, %d) in display %" PRId32 ".", x, y, displayId);
+                  "(%d, %d) in display %" PRId32 ".",
+                  x, y, displayId);
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
@@ -1453,19 +1419,19 @@
         /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (! mTempTouchState.down) {
+        if (!mTempTouchState.down) {
 #if DEBUG_FOCUS
             ALOGD("Dropping event because the pointer is not down or we previously "
-                    "dropped the pointer down event in display %" PRId32, displayId);
+                  "dropped the pointer down event in display %" PRId32,
+                  displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
 
         // Check whether touches should slip outside of the current foreground window.
-        if (maskedAction == AMOTION_EVENT_ACTION_MOVE
-                && entry->pointerCount == 1
-                && mTempTouchState.isSlippery()) {
+        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 &&
+            mTempTouchState.isSlippery()) {
             int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
             int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
@@ -1473,26 +1439,25 @@
                     mTempTouchState.getFirstForegroundWindowHandle();
             sp<InputWindowHandle> newTouchedWindowHandle =
                     findTouchedWindowAtLocked(displayId, x, y);
-            if (oldTouchedWindowHandle != newTouchedWindowHandle
-                    && oldTouchedWindowHandle != nullptr
-                    && newTouchedWindowHandle != nullptr) {
+            if (oldTouchedWindowHandle != newTouchedWindowHandle &&
+                oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
                 ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
-                        oldTouchedWindowHandle->getName().c_str(),
-                        newTouchedWindowHandle->getName().c_str(),
-                        displayId);
+                      oldTouchedWindowHandle->getName().c_str(),
+                      newTouchedWindowHandle->getName().c_str(), displayId);
 #endif
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
-                        InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
+                                                  InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+                                                  BitSet32(0));
 
                 // Make a slippery entrance into the new window.
                 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
                     isSplit = true;
                 }
 
-                int32_t targetFlags = InputTarget::FLAG_FOREGROUND
-                        | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+                int32_t targetFlags =
+                        InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
                 if (isSplit) {
                     targetFlags |= InputTarget::FLAG_SPLIT;
                 }
@@ -1514,20 +1479,22 @@
         if (mLastHoverWindowHandle != nullptr) {
 #if DEBUG_HOVER
             ALOGD("Sending hover exit event to window %s.",
-                    mLastHoverWindowHandle->getName().c_str());
+                  mLastHoverWindowHandle->getName().c_str());
 #endif
             mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
-                    InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
+                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
+                                              BitSet32(0));
         }
 
         // Let the new window know that the hover sequence is starting.
         if (newHoverWindowHandle != nullptr) {
 #if DEBUG_HOVER
             ALOGD("Sending hover enter event to window %s.",
-                    newHoverWindowHandle->getName().c_str());
+                  newHoverWindowHandle->getName().c_str());
 #endif
             mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
-                    InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
+                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+                                              BitSet32(0));
         }
     }
 
@@ -1538,8 +1505,7 @@
         for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
             if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
                 haveForegroundWindow = true;
-                if (! checkInjectionPermission(touchedWindow.windowHandle,
-                        entry->injectionState)) {
+                if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) {
                     injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
                     injectionPermission = INJECTION_PERMISSION_DENIED;
                     goto Failed;
@@ -1549,8 +1515,9 @@
         bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
         if (!haveForegroundWindow && !hasGestureMonitor) {
 #if DEBUG_FOCUS
-            ALOGD("Dropping event because there is no touched foreground window in display %"
-                    PRId32 " or gesture monitor to receive it.", displayId);
+            ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
+                  " or gesture monitor to receive it.",
+                  displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
@@ -1572,7 +1539,8 @@
                     sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
                     if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
                         mTempTouchState.addOrUpdateWindow(inputWindowHandle,
-                                InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+                                                          InputTarget::FLAG_ZERO_COORDS,
+                                                          BitSet32(0));
                     }
                 }
             }
@@ -1583,11 +1551,13 @@
     for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
         if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
             // Check whether the window is ready for more input.
-            std::string reason = checkWindowReadyForMoreInputLocked(currentTime,
-                    touchedWindow.windowHandle, entry, "touched");
+            std::string reason =
+                    checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
+                                                       entry, "touched");
             if (!reason.empty()) {
-                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                        nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
+                                                              touchedWindow.windowHandle,
+                                                              nextWakeupTime, reason.c_str());
                 goto Unresponsive;
             }
         }
@@ -1607,14 +1577,15 @@
                     getWindowHandlesLocked(displayId);
             for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
                 const InputWindowInfo* info = windowHandle->getInfo();
-                if (info->displayId == displayId
-                        && windowHandle->getInfo()->layoutParamsType
-                                == InputWindowInfo::TYPE_WALLPAPER) {
-                    mTempTouchState.addOrUpdateWindow(windowHandle,
-                            InputTarget::FLAG_WINDOW_IS_OBSCURED
-                                    | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED
-                                    | InputTarget::FLAG_DISPATCH_AS_IS,
-                            BitSet32(0));
+                if (info->displayId == displayId &&
+                    windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
+                    mTempTouchState
+                            .addOrUpdateWindow(windowHandle,
+                                               InputTarget::FLAG_WINDOW_IS_OBSCURED |
+                                                       InputTarget::
+                                                               FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
+                                                       InputTarget::FLAG_DISPATCH_AS_IS,
+                                               BitSet32(0));
                 }
             }
         }
@@ -1625,12 +1596,12 @@
 
     for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
         addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
-                touchedWindow.pointerIds, inputTargets);
+                              touchedWindow.pointerIds, inputTargets);
     }
 
     for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
         addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
-                touchedMonitor.yOffset, inputTargets);
+                                  touchedMonitor.yOffset, inputTargets);
     }
 
     // Drop the outside or hover touch windows since we will not care about them
@@ -1666,14 +1637,14 @@
                     *outConflictingPointerActions = true;
                 }
                 mTempTouchState.reset();
-                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
-                        || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+                    maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                     mTempTouchState.deviceId = entry->deviceId;
                     mTempTouchState.source = entry->source;
                     mTempTouchState.displayId = displayId;
                 }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_UP
-                    || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+                       maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
                 // All pointers up or canceled.
                 mTempTouchState.reset();
             } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
@@ -1690,7 +1661,7 @@
                     int32_t pointerIndex = getMotionEventActionPointerIndex(action);
                     uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
 
-                    for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+                    for (size_t i = 0; i < mTempTouchState.windows.size();) {
                         TouchedWindow& touchedWindow = mTempTouchState.windows[i];
                         if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
                             touchedWindow.pointerIds.clearBit(pointerId);
@@ -1735,14 +1706,15 @@
     updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
     ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
-            "timeSpentWaitingForApplication=%0.1fms",
-            injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
+          "timeSpentWaitingForApplication=%0.1fms",
+          injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
 
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
-        int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) {
+                                            int32_t targetFlags, BitSet32 pointerIds,
+                                            std::vector<InputTarget>& inputTargets) {
     sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
     if (inputChannel == nullptr) {
         ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
@@ -1753,8 +1725,8 @@
     InputTarget target;
     target.inputChannel = inputChannel;
     target.flags = targetFlags;
-    target.xOffset = - windowInfo->frameLeft;
-    target.yOffset = - windowInfo->frameTop;
+    target.xOffset = -windowInfo->frameLeft;
+    target.yOffset = -windowInfo->frameTop;
     target.globalScaleFactor = windowInfo->globalScaleFactor;
     target.windowXScale = windowInfo->windowXScale;
     target.windowYScale = windowInfo->windowYScale;
@@ -1763,8 +1735,8 @@
 }
 
 void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
-         int32_t displayId, float xOffset, float yOffset) {
-
+                                                       int32_t displayId, float xOffset,
+                                                       float yOffset) {
     std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
             mGlobalMonitorsByDisplay.find(displayId);
 
@@ -1776,8 +1748,9 @@
     }
 }
 
-void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor,
-        float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) {
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
+                                                float yOffset,
+                                                std::vector<InputTarget>& inputTargets) {
     InputTarget target;
     target.inputChannel = monitor.inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -1789,28 +1762,27 @@
 }
 
 bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
-        const InjectionState* injectionState) {
-    if (injectionState
-            && (windowHandle == nullptr
-                    || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
-            && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
+                                               const InjectionState* injectionState) {
+    if (injectionState &&
+        (windowHandle == nullptr ||
+         windowHandle->getInfo()->ownerUid != injectionState->injectorUid) &&
+        !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
         if (windowHandle != nullptr) {
             ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
-                    "owned by uid %d",
-                    injectionState->injectorPid, injectionState->injectorUid,
-                    windowHandle->getName().c_str(),
-                    windowHandle->getInfo()->ownerUid);
+                  "owned by uid %d",
+                  injectionState->injectorPid, injectionState->injectorUid,
+                  windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid);
         } else {
             ALOGW("Permission denied: injecting event from pid %d uid %d",
-                    injectionState->injectorPid, injectionState->injectorUid);
+                  injectionState->injectorPid, injectionState->injectorUid);
         }
         return false;
     }
     return true;
 }
 
-bool InputDispatcher::isWindowObscuredAtPointLocked(
-        const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
+                                                    int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
@@ -1819,16 +1791,14 @@
         }
 
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->displayId == displayId
-                && otherInfo->visible && !otherInfo->isTrustedOverlay()
-                && otherInfo->frameContainsPoint(x, y)) {
+        if (otherInfo->displayId == displayId && otherInfo->visible &&
+            !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
     }
     return false;
 }
 
-
 bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
@@ -1839,38 +1809,37 @@
         }
 
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->displayId == displayId
-                && otherInfo->visible && !otherInfo->isTrustedOverlay()
-                && otherInfo->overlaps(windowInfo)) {
+        if (otherInfo->displayId == displayId && otherInfo->visible &&
+            !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) {
             return true;
         }
     }
     return false;
 }
 
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
-        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
-        const char* targetType) {
+std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
+        nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
+        const EventEntry* eventEntry, const char* targetType) {
     // If the window is paused then keep waiting.
     if (windowHandle->getInfo()->paused) {
         return StringPrintf("Waiting because the %s window is paused.", targetType);
     }
 
     // If the window's connection is not registered then keep waiting.
-    ssize_t connectionIndex = getConnectionIndexLocked(
-            getInputChannelLocked(windowHandle->getToken()));
-    if (connectionIndex < 0) {
+    sp<Connection> connection =
+            getConnectionLocked(getInputChannelLocked(windowHandle->getToken()));
+    if (connection == nullptr) {
         return StringPrintf("Waiting because the %s window's input channel is not "
-                "registered with the input dispatcher.  The window may be in the process "
-                "of being removed.", targetType);
+                            "registered with the input dispatcher.  The window may be in the "
+                            "process of being removed.",
+                            targetType);
     }
 
     // If the connection is dead then keep waiting.
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
     if (connection->status != Connection::STATUS_NORMAL) {
         return StringPrintf("Waiting because the %s window's input connection is %s."
-                "The window may be in the process of being removed.", targetType,
-                connection->getStatusLabel());
+                            "The window may be in the process of being removed.",
+                            targetType, connection->getStatusLabel());
     }
 
     // If the connection is backed up then keep waiting.
@@ -1970,25 +1939,25 @@
 
     int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
     switch (eventEntry->type) {
-    case EventEntry::TYPE_MOTION: {
-        const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
-        if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
-            return;
-        }
+        case EventEntry::TYPE_MOTION: {
+            const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
+            if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+                return;
+            }
 
-        if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
-            eventType = USER_ACTIVITY_EVENT_TOUCH;
+            if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
+                eventType = USER_ACTIVITY_EVENT_TOUCH;
+            }
+            break;
         }
-        break;
-    }
-    case EventEntry::TYPE_KEY: {
-        const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
-        if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
-            return;
+        case EventEntry::TYPE_KEY: {
+            const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
+            if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+                return;
+            }
+            eventType = USER_ACTIVITY_EVENT_BUTTON;
+            break;
         }
-        eventType = USER_ACTIVITY_EVENT_BUTTON;
-        break;
-    }
     }
 
     std::unique_ptr<CommandEntry> commandEntry =
@@ -1999,22 +1968,22 @@
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+                                                 const sp<Connection>& connection,
+                                                 EventEntry* eventEntry,
+                                                 const InputTarget* inputTarget) {
     if (ATRACE_ENABLED()) {
-        std::string message = StringPrintf(
-                "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
-                connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+        std::string message =
+                StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+                             connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
         ATRACE_NAME(message.c_str());
     }
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-            "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
-            "windowScaleFactor=(%f, %f), pointerIds=0x%x",
-            connection->getInputChannelName().c_str(), inputTarget->flags,
-            inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->globalScaleFactor,
-            inputTarget->windowXScale, inputTarget->windowYScale,
-            inputTarget->pointerIds.value);
+          "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+          "windowScaleFactor=(%f, %f), pointerIds=0x%x",
+          connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
+          inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
+          inputTarget->windowYScale, inputTarget->pointerIds.value);
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -2022,7 +1991,7 @@
     if (connection->status != Connection::STATUS_NORMAL) {
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->getInputChannelName().c_str(), connection->getStatusLabel());
+              connection->getInputChannelName().c_str(), connection->getStatusLabel());
 #endif
         return;
     }
@@ -2033,18 +2002,16 @@
 
         MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
         if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
-            MotionEntry* splitMotionEntry = splitMotionEvent(
-                    originalMotionEntry, inputTarget->pointerIds);
+            MotionEntry* splitMotionEntry =
+                    splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
             if (!splitMotionEntry) {
                 return; // split event was dropped
             }
 #if DEBUG_FOCUS
-            ALOGD("channel '%s' ~ Split motion event.",
-                    connection->getInputChannelName().c_str());
+            ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str());
             logOutboundMotionDetails("  ", splitMotionEntry);
 #endif
-            enqueueDispatchEntriesLocked(currentTime, connection,
-                    splitMotionEntry, inputTarget);
+            enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
             splitMotionEntry->release();
             return;
         }
@@ -2055,11 +2022,14 @@
 }
 
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+                                                   const sp<Connection>& connection,
+                                                   EventEntry* eventEntry,
+                                                   const InputTarget* inputTarget) {
     if (ATRACE_ENABLED()) {
-        std::string message = StringPrintf(
-                "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
-                connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+        std::string message =
+                StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
+                             ")",
+                             connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
         ATRACE_NAME(message.c_str());
     }
 
@@ -2067,17 +2037,17 @@
 
     // Enqueue dispatch entries for the requested modes.
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
+                               InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
+                               InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
+                               InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_IS);
+                               InputTarget::FLAG_DISPATCH_AS_IS);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
+                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty && !connection->outboundQueue.empty()) {
@@ -2085,14 +2055,14 @@
     }
 }
 
-void InputDispatcher::enqueueDispatchEntryLocked(
-        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
-        int32_t dispatchMode) {
+void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
+                                                 EventEntry* eventEntry,
+                                                 const InputTarget* inputTarget,
+                                                 int32_t dispatchMode) {
     if (ATRACE_ENABLED()) {
-        std::string message = StringPrintf(
-                "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
-                connection->getInputChannelName().c_str(),
-                dispatchModeToString(dispatchMode).c_str());
+        std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
+                                           connection->getInputChannelName().c_str(),
+                                           dispatchModeToString(dispatchMode).c_str());
         ATRACE_NAME(message.c_str());
     }
     int32_t inputTargetFlags = inputTarget->flags;
@@ -2103,78 +2073,81 @@
 
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
-    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
-            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->globalScaleFactor, inputTarget->windowXScale,
-            inputTarget->windowYScale);
+    DispatchEntry* dispatchEntry =
+            new DispatchEntry(eventEntry, // increments ref
+                              inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
+                              inputTarget->globalScaleFactor, inputTarget->windowXScale,
+                              inputTarget->windowYScale);
 
     // Apply target flags and update the connection's input state.
     switch (eventEntry->type) {
-    case EventEntry::TYPE_KEY: {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-        dispatchEntry->resolvedAction = keyEntry->action;
-        dispatchEntry->resolvedFlags = keyEntry->flags;
+        case EventEntry::TYPE_KEY: {
+            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+            dispatchEntry->resolvedAction = keyEntry->action;
+            dispatchEntry->resolvedFlags = keyEntry->flags;
 
-        if (!connection->inputState.trackKey(keyEntry,
-                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+            if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
+                                                 dispatchEntry->resolvedFlags)) {
 #if DEBUG_DISPATCH_CYCLE
-            ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
-                    connection->getInputChannelName().c_str());
+                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
+                      connection->getInputChannelName().c_str());
 #endif
-            delete dispatchEntry;
-            return; // skip the inconsistent event
+                delete dispatchEntry;
+                return; // skip the inconsistent event
+            }
+            break;
         }
-        break;
-    }
 
-    case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-        if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
-        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
-        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
-        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
-        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
-        } else {
-            dispatchEntry->resolvedAction = motionEntry->action;
-        }
-        if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
-                && !connection->inputState.isHovering(
-                        motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
+        case EventEntry::TYPE_MOTION: {
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+            if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+            } else {
+                dispatchEntry->resolvedAction = motionEntry->action;
+            }
+            if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
+                !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source,
+                                                   motionEntry->displayId)) {
 #if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
-                connection->getInputChannelName().c_str());
+                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
+                      "event",
+                      connection->getInputChannelName().c_str());
 #endif
-            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
-        }
+                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+            }
 
-        dispatchEntry->resolvedFlags = motionEntry->flags;
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
-            dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-        }
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
-            dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
-        }
+            dispatchEntry->resolvedFlags = motionEntry->flags;
+            if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+                dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+            }
+            if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+                dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+            }
 
-        if (!connection->inputState.trackMotion(motionEntry,
-                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+            if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
+                                                    dispatchEntry->resolvedFlags)) {
 #if DEBUG_DISPATCH_CYCLE
-            ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
-                    connection->getInputChannelName().c_str());
+                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+                      "event",
+                      connection->getInputChannelName().c_str());
 #endif
-            delete dispatchEntry;
-            return; // skip the inconsistent event
+                delete dispatchEntry;
+                return; // skip the inconsistent event
+            }
+
+            dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction,
+                                            inputTarget->inputChannel->getToken());
+
+            break;
         }
-
-        dispatchPointerDownOutsideFocus(motionEntry->source,
-                dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
-
-        break;
-    }
     }
 
     // Remember that we are waiting for this dispatch to complete.
@@ -2185,11 +2158,10 @@
     // Enqueue the dispatch entry.
     connection->outboundQueue.push_back(dispatchEntry);
     traceOutboundQueueLength(connection);
-
 }
 
 void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
-        const sp<IBinder>& newToken) {
+                                                      const sp<IBinder>& newToken) {
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
     uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
     if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
@@ -2217,15 +2189,14 @@
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
+                                               const sp<Connection>& connection) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
-                connection->getInputChannelName().c_str());
+                                           connection->getInputChannelName().c_str());
         ATRACE_NAME(message.c_str());
     }
 #if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ startDispatchCycle",
-            connection->getInputChannelName().c_str());
+    ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
 #endif
 
     while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
@@ -2236,76 +2207,79 @@
         status_t status;
         EventEntry* eventEntry = dispatchEntry->eventEntry;
         switch (eventEntry->type) {
-        case EventEntry::TYPE_KEY: {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+            case EventEntry::TYPE_KEY: {
+                KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
-            // Publish the key event.
-            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
-                    keyEntry->deviceId, keyEntry->source, keyEntry->displayId,
-                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
-                    keyEntry->keyCode, keyEntry->scanCode,
-                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
-                    keyEntry->eventTime);
-            break;
-        }
-
-        case EventEntry::TYPE_MOTION: {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-
-            PointerCoords scaledCoords[MAX_POINTERS];
-            const PointerCoords* usingCoords = motionEntry->pointerCoords;
-
-            // Set the X and Y offset depending on the input source.
-            float xOffset, yOffset;
-            if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
-                    && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-                float globalScaleFactor = dispatchEntry->globalScaleFactor;
-                float wxs = dispatchEntry->windowXScale;
-                float wys = dispatchEntry->windowYScale;
-                xOffset = dispatchEntry->xOffset * wxs;
-                yOffset = dispatchEntry->yOffset * wys;
-                if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
-                    for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
-                        scaledCoords[i] = motionEntry->pointerCoords[i];
-                        scaledCoords[i].scale(globalScaleFactor, wxs, wys);
-                    }
-                    usingCoords = scaledCoords;
-                }
-            } else {
-                xOffset = 0.0f;
-                yOffset = 0.0f;
-
-                // We don't want the dispatch target to know.
-                if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
-                    for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
-                        scaledCoords[i].clear();
-                    }
-                    usingCoords = scaledCoords;
-                }
+                // Publish the key event.
+                status = connection->inputPublisher
+                                 .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
+                                                  keyEntry->source, keyEntry->displayId,
+                                                  dispatchEntry->resolvedAction,
+                                                  dispatchEntry->resolvedFlags, keyEntry->keyCode,
+                                                  keyEntry->scanCode, keyEntry->metaState,
+                                                  keyEntry->repeatCount, keyEntry->downTime,
+                                                  keyEntry->eventTime);
+                break;
             }
 
-            // Publish the motion event.
-            status =
-                    connection->inputPublisher
-                            .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
-                                                motionEntry->source, motionEntry->displayId,
-                                                dispatchEntry->resolvedAction,
-                                                motionEntry->actionButton,
-                                                dispatchEntry->resolvedFlags,
-                                                motionEntry->edgeFlags, motionEntry->metaState,
-                                                motionEntry->buttonState,
-                                                motionEntry->classification, xOffset, yOffset,
-                                                motionEntry->xPrecision, motionEntry->yPrecision,
-                                                motionEntry->xCursorPosition,
-                                                motionEntry->yCursorPosition, motionEntry->downTime,
-                                                motionEntry->eventTime, motionEntry->pointerCount,
-                                                motionEntry->pointerProperties, usingCoords);
-            break;
-        }
+            case EventEntry::TYPE_MOTION: {
+                MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
-        default:
-            ALOG_ASSERT(false);
-            return;
+                PointerCoords scaledCoords[MAX_POINTERS];
+                const PointerCoords* usingCoords = motionEntry->pointerCoords;
+
+                // Set the X and Y offset depending on the input source.
+                float xOffset, yOffset;
+                if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+                    !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+                    float globalScaleFactor = dispatchEntry->globalScaleFactor;
+                    float wxs = dispatchEntry->windowXScale;
+                    float wys = dispatchEntry->windowYScale;
+                    xOffset = dispatchEntry->xOffset * wxs;
+                    yOffset = dispatchEntry->yOffset * wys;
+                    if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+                        for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+                            scaledCoords[i] = motionEntry->pointerCoords[i];
+                            scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+                        }
+                        usingCoords = scaledCoords;
+                    }
+                } else {
+                    xOffset = 0.0f;
+                    yOffset = 0.0f;
+
+                    // We don't want the dispatch target to know.
+                    if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+                        for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+                            scaledCoords[i].clear();
+                        }
+                        usingCoords = scaledCoords;
+                    }
+                }
+
+                // Publish the motion event.
+                status = connection->inputPublisher
+                                 .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
+                                                     motionEntry->source, motionEntry->displayId,
+                                                     dispatchEntry->resolvedAction,
+                                                     motionEntry->actionButton,
+                                                     dispatchEntry->resolvedFlags,
+                                                     motionEntry->edgeFlags, motionEntry->metaState,
+                                                     motionEntry->buttonState,
+                                                     motionEntry->classification, xOffset, yOffset,
+                                                     motionEntry->xPrecision,
+                                                     motionEntry->yPrecision,
+                                                     motionEntry->xCursorPosition,
+                                                     motionEntry->yCursorPosition,
+                                                     motionEntry->downTime, motionEntry->eventTime,
+                                                     motionEntry->pointerCount,
+                                                     motionEntry->pointerProperties, usingCoords);
+                break;
+            }
+
+            default:
+                ALOG_ASSERT(false);
+                return;
         }
 
         // Check the result.
@@ -2313,24 +2287,25 @@
             if (status == WOULD_BLOCK) {
                 if (connection->waitQueue.empty()) {
                     ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
-                            "This is unexpected because the wait queue is empty, so the pipe "
-                            "should be empty and we shouldn't have any problems writing an "
-                            "event to it, status=%d", connection->getInputChannelName().c_str(),
-                            status);
+                          "This is unexpected because the wait queue is empty, so the pipe "
+                          "should be empty and we shouldn't have any problems writing an "
+                          "event to it, status=%d",
+                          connection->getInputChannelName().c_str(), status);
                     abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                 } else {
                     // Pipe is full and we are waiting for the app to finish process some events
                     // before sending more events to it.
 #if DEBUG_DISPATCH_CYCLE
                     ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
-                            "waiting for the application to catch up",
-                            connection->getInputChannelName().c_str());
+                          "waiting for the application to catch up",
+                          connection->getInputChannelName().c_str());
 #endif
                     connection->inputPublisherBlocked = true;
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
-                        "status=%d", connection->getInputChannelName().c_str(), status);
+                      "status=%d",
+                      connection->getInputChannelName().c_str(), status);
                 abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             }
             return;
@@ -2347,16 +2322,17 @@
 }
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, uint32_t seq, bool handled) {
+                                                const sp<Connection>& connection, uint32_t seq,
+                                                bool handled) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
-            connection->getInputChannelName().c_str(), seq, toString(handled));
+          connection->getInputChannelName().c_str(), seq, toString(handled));
 #endif
 
     connection->inputPublisherBlocked = false;
 
-    if (connection->status == Connection::STATUS_BROKEN
-            || connection->status == Connection::STATUS_ZOMBIE) {
+    if (connection->status == Connection::STATUS_BROKEN ||
+        connection->status == Connection::STATUS_ZOMBIE) {
         return;
     }
 
@@ -2365,10 +2341,11 @@
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, bool notify) {
+                                                     const sp<Connection>& connection,
+                                                     bool notify) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
-            connection->getInputChannelName().c_str(), toString(notify));
+          connection->getInputChannelName().c_str(), toString(notify));
 #endif
 
     // Clear the dispatch queues.
@@ -2410,19 +2387,20 @@
     { // acquire lock
         std::scoped_lock _l(d->mLock);
 
-        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
-        if (connectionIndex < 0) {
+        if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
             ALOGE("Received spurious receive callback for unknown input channel.  "
-                    "fd=%d, events=0x%x", fd, events);
+                  "fd=%d, events=0x%x",
+                  fd, events);
             return 0; // remove the callback
         }
 
         bool notify;
-        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
+        sp<Connection> connection = d->mConnectionsByFd[fd];
         if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
             if (!(events & ALOOPER_EVENT_INPUT)) {
                 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                        "events=0x%x", connection->getInputChannelName().c_str(), events);
+                      "events=0x%x",
+                      connection->getInputChannelName().c_str(), events);
                 return 1;
             }
 
@@ -2449,7 +2427,7 @@
             notify = status != DEAD_OBJECT || !connection->monitor;
             if (notify) {
                 ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
-                        connection->getInputChannelName().c_str(), status);
+                      connection->getInputChannelName().c_str(), status);
             }
         } else {
             // Monitor channels are never explicitly unregistered.
@@ -2458,25 +2436,25 @@
             notify = !connection->monitor;
             if (notify) {
                 ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                        "events=0x%x", connection->getInputChannelName().c_str(), events);
+                      "events=0x%x",
+                      connection->getInputChannelName().c_str(), events);
             }
         }
 
         // Unregister the channel.
         d->unregisterInputChannelLocked(connection->inputChannel, notify);
         return 0; // remove the callback
-    } // release lock
+    }             // release lock
 }
 
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked (
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        synthesizeCancelationEventsForConnectionLocked(
-                mConnectionsByFd.valueAt(i), options);
+    for (const auto& pair : mConnectionsByFd) {
+        synthesizeCancelationEventsForConnectionLocked(pair.second, options);
     }
 }
 
-void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
         const CancelationOptions& options) {
     synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay);
     synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay);
@@ -2495,11 +2473,12 @@
 
 void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
         const sp<InputChannel>& channel, const CancelationOptions& options) {
-    ssize_t index = getConnectionIndexLocked(channel);
-    if (index >= 0) {
-        synthesizeCancelationEventsForConnectionLocked(
-                mConnectionsByFd.valueAt(index), options);
+    sp<Connection> connection = getConnectionLocked(channel);
+    if (connection == nullptr) {
+        return;
     }
+
+    synthesizeCancelationEventsForConnectionLocked(connection, options);
 }
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
@@ -2511,32 +2490,31 @@
     nsecs_t currentTime = now();
 
     std::vector<EventEntry*> cancelationEvents;
-    connection->inputState.synthesizeCancelationEvents(currentTime,
-            cancelationEvents, options);
+    connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options);
 
     if (!cancelationEvents.empty()) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-                "with reality: %s, mode=%d.",
-                connection->getInputChannelName().c_str(), cancelationEvents.size(),
-                options.reason, options.mode);
+              "with reality: %s, mode=%d.",
+              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+              options.mode);
 #endif
         for (size_t i = 0; i < cancelationEvents.size(); i++) {
             EventEntry* cancelationEventEntry = cancelationEvents[i];
             switch (cancelationEventEntry->type) {
-            case EventEntry::TYPE_KEY:
-                logOutboundKeyDetails("cancel - ",
-                        static_cast<KeyEntry*>(cancelationEventEntry));
-                break;
-            case EventEntry::TYPE_MOTION:
-                logOutboundMotionDetails("cancel - ",
-                        static_cast<MotionEntry*>(cancelationEventEntry));
-                break;
+                case EventEntry::TYPE_KEY:
+                    logOutboundKeyDetails("cancel - ",
+                                          static_cast<KeyEntry*>(cancelationEventEntry));
+                    break;
+                case EventEntry::TYPE_MOTION:
+                    logOutboundMotionDetails("cancel - ",
+                                             static_cast<MotionEntry*>(cancelationEventEntry));
+                    break;
             }
 
             InputTarget target;
-            sp<InputWindowHandle> windowHandle = getWindowHandleLocked(
-                    connection->inputChannel->getToken());
+            sp<InputWindowHandle> windowHandle =
+                    getWindowHandleLocked(connection->inputChannel->getToken());
             if (windowHandle != nullptr) {
                 const InputWindowInfo* windowInfo = windowHandle->getInfo();
                 target.xOffset = -windowInfo->frameLeft;
@@ -2553,7 +2531,7 @@
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
             enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                    &target, InputTarget::FLAG_DISPATCH_AS_IS);
+                                       &target, InputTarget::FLAG_DISPATCH_AS_IS);
 
             cancelationEventEntry->release();
         }
@@ -2562,8 +2540,8 @@
     }
 }
 
-InputDispatcher::MotionEntry*
-InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry,
+                                               BitSet32 pointerIds) {
     ALOG_ASSERT(pointerIds.value != 0);
 
     uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -2574,7 +2552,7 @@
     uint32_t splitPointerCount = 0;
 
     for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
-            originalPointerIndex++) {
+         originalPointerIndex++) {
         const PointerProperties& pointerProperties =
                 originalMotionEntry->pointerProperties[originalPointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
@@ -2594,16 +2572,16 @@
         // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
         // in this way.
         ALOGW("Dropping split motion event because the pointer count is %d but "
-                "we expected there to be %d pointers.  This probably means we received "
-                "a broken sequence of pointer ids from the input device.",
-                splitPointerCount, pointerIds.count());
+              "we expected there to be %d pointers.  This probably means we received "
+              "a broken sequence of pointer ids from the input device.",
+              splitPointerCount, pointerIds.count());
         return nullptr;
     }
 
     int32_t action = originalMotionEntry->action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
-    if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
-            || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+    if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
+        maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
         int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
         const PointerProperties& pointerProperties =
                 originalMotionEntry->pointerProperties[originalPointerIndex];
@@ -2612,15 +2590,16 @@
             if (pointerIds.count() == 1) {
                 // The first/last pointer went down/up.
                 action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
-                        ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+                        ? AMOTION_EVENT_ACTION_DOWN
+                        : AMOTION_EVENT_ACTION_UP;
             } else {
                 // A secondary pointer went down/up.
                 uint32_t splitPointerIndex = 0;
                 while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) {
                     splitPointerIndex += 1;
                 }
-                action = maskedAction | (splitPointerIndex
-                        << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+                action = maskedAction |
+                        (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
             }
         } else {
             // An unrelated pointer changed.
@@ -2674,7 +2653,7 @@
  * This will potentially overwrite keyCode and metaState.
  */
 void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
-        int32_t& keyCode, int32_t& metaState) {
+                                              int32_t& keyCode, int32_t& metaState) {
     if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
         int32_t newKeyCode = AKEYCODE_UNKNOWN;
         if (keyCode == AKEYCODE_DEL) {
@@ -2706,12 +2685,12 @@
 
 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyKey - eventTime=%" PRId64
-            ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
-            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
-            args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
-            args->action, args->flags, args->keyCode, args->scanCode,
-            args->metaState, args->downTime);
+    ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+          "policyFlags=0x%x, action=0x%x, "
+          "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+          args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+          args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
+          args->downTime);
 #endif
     if (!validateKeyEvent(args->action)) {
         return;
@@ -2737,15 +2716,14 @@
     accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->deviceId, args->source, args->displayId, args->action,
-            flags, keyCode, args->scanCode, metaState, repeatCount,
-            args->downTime, args->eventTime);
+    event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
+                     args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
 
     android::base::Timer t;
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
-                std::to_string(t.duration().count()).c_str());
+              std::to_string(t.duration().count()).c_str());
     }
 
     bool needWake;
@@ -2763,10 +2741,10 @@
             mLock.lock();
         }
 
-        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
-                args->deviceId, args->source, args->displayId, policyFlags,
-                args->action, flags, keyCode, args->scanCode,
-                metaState, repeatCount, args->downTime);
+        KeyEntry* newEntry =
+                new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source,
+                             args->displayId, policyFlags, args->action, flags, keyCode,
+                             args->scanCode, metaState, repeatCount, args->downTime);
 
         needWake = enqueueInboundEventLocked(newEntry);
         mLock.unlock();
@@ -2794,24 +2772,23 @@
           args->yCursorPosition, args->downTime);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
-                "x=%f, y=%f, pressure=%f, size=%f, "
-                "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-                "orientation=%f",
-                i, args->pointerProperties[i].id,
-                args->pointerProperties[i].toolType,
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+              "x=%f, y=%f, pressure=%f, size=%f, "
+              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+              "orientation=%f",
+              i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
-    if (!validateMotionEvent(args->action, args->actionButton,
-                args->pointerCount, args->pointerProperties)) {
+    if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+                             args->pointerProperties)) {
         return;
     }
 
@@ -2822,7 +2799,7 @@
     mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
-                std::to_string(t.duration().count()).c_str());
+              std::to_string(t.duration().count()).c_str());
     }
 
     bool needWake;
@@ -2874,20 +2851,19 @@
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
-            "switchMask=0x%08x",
-            args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+          "switchMask=0x%08x",
+          args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
 #endif
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->notifySwitch(args->eventTime,
-            args->switchValues, args->switchMask, policyFlags);
+    mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags);
 }
 
 void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d",
-            args->eventTime, args->deviceId);
+    ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+          args->deviceId);
 #endif
 
     bool needWake;
@@ -2904,13 +2880,13 @@
     }
 }
 
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
-        uint32_t policyFlags) {
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
+                                          int32_t injectorUid, int32_t syncMode,
+                                          int32_t timeoutMillis, uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-            "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
-            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
+          "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+          event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
 #endif
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -2922,87 +2898,74 @@
 
     std::queue<EventEntry*> injectedEntries;
     switch (event->getType()) {
-    case AINPUT_EVENT_TYPE_KEY: {
-        KeyEvent keyEvent;
-        keyEvent.initialize(*static_cast<const KeyEvent*>(event));
-        int32_t action = keyEvent.getAction();
-        if (! validateKeyEvent(action)) {
-            return INPUT_EVENT_INJECTION_FAILED;
-        }
-
-        int32_t flags = keyEvent.getFlags();
-        int32_t keyCode = keyEvent.getKeyCode();
-        int32_t metaState = keyEvent.getMetaState();
-        accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
-                /*byref*/ keyCode, /*byref*/ metaState);
-        keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
-            action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
-            keyEvent.getDownTime(), keyEvent.getEventTime());
-
-        if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
-            policyFlags |= POLICY_FLAG_VIRTUAL;
-        }
-
-        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
-            android::base::Timer t;
-            mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
-            if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
-                ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
-                        std::to_string(t.duration().count()).c_str());
+        case AINPUT_EVENT_TYPE_KEY: {
+            KeyEvent keyEvent;
+            keyEvent.initialize(*static_cast<const KeyEvent*>(event));
+            int32_t action = keyEvent.getAction();
+            if (!validateKeyEvent(action)) {
+                return INPUT_EVENT_INJECTION_FAILED;
             }
-        }
 
-        mLock.lock();
-        KeyEntry* injectedEntry =
-                new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
-                             keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
-                             policyFlags, action, flags, keyEvent.getKeyCode(),
-                             keyEvent.getScanCode(), keyEvent.getMetaState(),
-                             keyEvent.getRepeatCount(), keyEvent.getDownTime());
-        injectedEntries.push(injectedEntry);
-        break;
-    }
+            int32_t flags = keyEvent.getFlags();
+            int32_t keyCode = keyEvent.getKeyCode();
+            int32_t metaState = keyEvent.getMetaState();
+            accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+                                    /*byref*/ keyCode, /*byref*/ metaState);
+            keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
+                                keyEvent.getDisplayId(), action, flags, keyCode,
+                                keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
+                                keyEvent.getDownTime(), keyEvent.getEventTime());
 
-    case AINPUT_EVENT_TYPE_MOTION: {
-        const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-        int32_t action = motionEvent->getAction();
-        size_t pointerCount = motionEvent->getPointerCount();
-        const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
-        int32_t actionButton = motionEvent->getActionButton();
-        int32_t displayId = motionEvent->getDisplayId();
-        if (! validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
-            return INPUT_EVENT_INJECTION_FAILED;
-        }
-
-        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
-            nsecs_t eventTime = motionEvent->getEventTime();
-            android::base::Timer t;
-            mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
-            if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
-                ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
-                        std::to_string(t.duration().count()).c_str());
+            if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
+                policyFlags |= POLICY_FLAG_VIRTUAL;
             }
+
+            if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+                android::base::Timer t;
+                mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
+                if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+                    ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
+                          std::to_string(t.duration().count()).c_str());
+                }
+            }
+
+            mLock.lock();
+            KeyEntry* injectedEntry =
+                    new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
+                                 keyEvent.getDeviceId(), keyEvent.getSource(),
+                                 keyEvent.getDisplayId(), policyFlags, action, flags,
+                                 keyEvent.getKeyCode(), keyEvent.getScanCode(),
+                                 keyEvent.getMetaState(), keyEvent.getRepeatCount(),
+                                 keyEvent.getDownTime());
+            injectedEntries.push(injectedEntry);
+            break;
         }
 
-        mLock.lock();
-        const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
-        const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-        MotionEntry* injectedEntry =
-                new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                motionEvent->getDeviceId(), motionEvent->getSource(),
-                                motionEvent->getDisplayId(), policyFlags, action, actionButton,
-                                motionEvent->getFlags(), motionEvent->getMetaState(),
-                                motionEvent->getButtonState(), motionEvent->getClassification(),
-                                motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(),
-                                motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(),
-                                uint32_t(pointerCount), pointerProperties, samplePointerCoords,
-                                motionEvent->getXOffset(), motionEvent->getYOffset());
-        injectedEntries.push(injectedEntry);
-        for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
-            sampleEventTimes += 1;
-            samplePointerCoords += pointerCount;
-            MotionEntry* nextInjectedEntry =
+        case AINPUT_EVENT_TYPE_MOTION: {
+            const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+            int32_t action = motionEvent->getAction();
+            size_t pointerCount = motionEvent->getPointerCount();
+            const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
+            int32_t actionButton = motionEvent->getActionButton();
+            int32_t displayId = motionEvent->getDisplayId();
+            if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
+                return INPUT_EVENT_INJECTION_FAILED;
+            }
+
+            if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+                nsecs_t eventTime = motionEvent->getEventTime();
+                android::base::Timer t;
+                mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+                if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+                    ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
+                          std::to_string(t.duration().count()).c_str());
+                }
+            }
+
+            mLock.lock();
+            const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+            const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+            MotionEntry* injectedEntry =
                     new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
                                     motionEvent->getDeviceId(), motionEvent->getSource(),
                                     motionEvent->getDisplayId(), policyFlags, action, actionButton,
@@ -3015,14 +2978,32 @@
                                     motionEvent->getDownTime(), uint32_t(pointerCount),
                                     pointerProperties, samplePointerCoords,
                                     motionEvent->getXOffset(), motionEvent->getYOffset());
-            injectedEntries.push(nextInjectedEntry);
+            injectedEntries.push(injectedEntry);
+            for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+                sampleEventTimes += 1;
+                samplePointerCoords += pointerCount;
+                MotionEntry* nextInjectedEntry =
+                        new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
+                                        motionEvent->getDeviceId(), motionEvent->getSource(),
+                                        motionEvent->getDisplayId(), policyFlags, action,
+                                        actionButton, motionEvent->getFlags(),
+                                        motionEvent->getMetaState(), motionEvent->getButtonState(),
+                                        motionEvent->getClassification(),
+                                        motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
+                                        motionEvent->getYPrecision(),
+                                        motionEvent->getRawXCursorPosition(),
+                                        motionEvent->getRawYCursorPosition(),
+                                        motionEvent->getDownTime(), uint32_t(pointerCount),
+                                        pointerProperties, samplePointerCoords,
+                                        motionEvent->getXOffset(), motionEvent->getYOffset());
+                injectedEntries.push(nextInjectedEntry);
+            }
+            break;
         }
-        break;
-    }
 
-    default:
-        ALOGW("Cannot inject event of type %d", event->getType());
-        return INPUT_EVENT_INJECTION_FAILED;
+        default:
+            ALOGW("Cannot inject event of type %d", event->getType());
+            return INPUT_EVENT_INJECTION_FAILED;
     }
 
     InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
@@ -3062,7 +3043,7 @@
                 if (remainingTimeout <= 0) {
 #if DEBUG_INJECTION
                     ALOGD("injectInputEvent - Timed out waiting for injection result "
-                            "to become available.");
+                          "to become available.");
 #endif
                     injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
                     break;
@@ -3071,18 +3052,18 @@
                 mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
             }
 
-            if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
-                    && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+            if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED &&
+                syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
                 while (injectionState->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
                     ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                            injectionState->pendingForegroundDispatches);
+                          injectionState->pendingForegroundDispatches);
 #endif
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
 #if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Timed out waiting for pending foreground "
-                            "dispatches to finish.");
+                        ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+                              "dispatches to finish.");
 #endif
                         injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
                         break;
@@ -3098,16 +3079,16 @@
 
 #if DEBUG_INJECTION
     ALOGD("injectInputEvent - Finished with result %d.  "
-            "injectorPid=%d, injectorUid=%d",
-            injectionResult, injectorPid, injectorUid);
+          "injectorPid=%d, injectorUid=%d",
+          injectionResult, injectorPid, injectorUid);
 #endif
 
     return injectionResult;
 }
 
 bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
-    return injectorUid == 0
-            || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+    return injectorUid == 0 ||
+            mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
 }
 
 void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
@@ -3115,26 +3096,25 @@
     if (injectionState) {
 #if DEBUG_INJECTION
         ALOGD("Setting input event injection result to %d.  "
-                "injectorPid=%d, injectorUid=%d",
-                 injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+              "injectorPid=%d, injectorUid=%d",
+              injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (injectionState->injectionIsAsync
-                && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
+        if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
-            case INPUT_EVENT_INJECTION_SUCCEEDED:
-                ALOGV("Asynchronous input event injection succeeded.");
-                break;
-            case INPUT_EVENT_INJECTION_FAILED:
-                ALOGW("Asynchronous input event injection failed.");
-                break;
-            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
-                ALOGW("Asynchronous input event injection permission denied.");
-                break;
-            case INPUT_EVENT_INJECTION_TIMED_OUT:
-                ALOGW("Asynchronous input event injection timed out.");
-                break;
+                case INPUT_EVENT_INJECTION_SUCCEEDED:
+                    ALOGV("Asynchronous input event injection succeeded.");
+                    break;
+                case INPUT_EVENT_INJECTION_FAILED:
+                    ALOGW("Asynchronous input event injection failed.");
+                    break;
+                case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+                    ALOGW("Asynchronous input event injection permission denied.");
+                    break;
+                case INPUT_EVENT_INJECTION_TIMED_OUT:
+                    ALOGW("Asynchronous input event injection timed out.");
+                    break;
             }
         }
 
@@ -3186,9 +3166,9 @@
             if (handle->getToken() == windowHandle->getToken()) {
                 if (windowHandle->getInfo()->displayId != it.first) {
                     ALOGE("Found window %s in display %" PRId32
-                            ", but it should belong to display %" PRId32,
-                            windowHandle->getName().c_str(), it.first,
-                            windowHandle->getInfo()->displayId);
+                          ", but it should belong to display %" PRId32,
+                          windowHandle->getName().c_str(), it.first,
+                          windowHandle->getInfo()->displayId);
                 }
                 return true;
             }
@@ -3270,7 +3250,8 @@
  * For removed handle, check if need to send a cancel event if already in touch.
  */
 void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
-        int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+                                      int32_t displayId,
+                                      const sp<ISetInputWindowsListener>& setInputWindowsListener) {
 #if DEBUG_FOCUS
     ALOGD("setInputWindows displayId=%" PRId32, displayId);
 #endif
@@ -3307,22 +3288,21 @@
             if (oldFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
                 ALOGD("Focus left window: %s in display %" PRId32,
-                        oldFocusedWindowHandle->getName().c_str(), displayId);
+                      oldFocusedWindowHandle->getName().c_str(), displayId);
 #endif
-                sp<InputChannel> focusedInputChannel = getInputChannelLocked(
-                        oldFocusedWindowHandle->getToken());
+                sp<InputChannel> focusedInputChannel =
+                        getInputChannelLocked(oldFocusedWindowHandle->getToken());
                 if (focusedInputChannel != nullptr) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
-                            "focus left window");
-                    synthesizeCancelationEventsForInputChannelLocked(
-                            focusedInputChannel, options);
+                                               "focus left window");
+                    synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
                 }
                 mFocusedWindowHandlesByDisplay.erase(displayId);
             }
             if (newFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
                 ALOGD("Focus entered window: %s in display %" PRId32,
-                        newFocusedWindowHandle->getName().c_str(), displayId);
+                      newFocusedWindowHandle->getName().c_str(), displayId);
 #endif
                 mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
             }
@@ -3330,30 +3310,29 @@
             if (mFocusedDisplayId == displayId) {
                 onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
             }
-
         }
 
         ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
         if (stateIndex >= 0) {
             TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
-            for (size_t i = 0; i < state.windows.size(); ) {
+            for (size_t i = 0; i < state.windows.size();) {
                 TouchedWindow& touchedWindow = state.windows[i];
                 if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
 #if DEBUG_FOCUS
                     ALOGD("Touched window was removed: %s in display %" PRId32,
-                            touchedWindow.windowHandle->getName().c_str(), displayId);
+                          touchedWindow.windowHandle->getName().c_str(), displayId);
 #endif
                     sp<InputChannel> touchedInputChannel =
                             getInputChannelLocked(touchedWindow.windowHandle->getToken());
                     if (touchedInputChannel != nullptr) {
                         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                                "touched window was removed");
-                        synthesizeCancelationEventsForInputChannelLocked(
-                                touchedInputChannel, options);
+                                                   "touched window was removed");
+                        synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel,
+                                                                         options);
                     }
                     state.windows.erase(state.windows.begin() + i);
                 } else {
-                  ++i;
+                    ++i;
                 }
             }
         }
@@ -3404,7 +3383,7 @@
         }
 
 #if DEBUG_FOCUS
-        //logDispatchStateLocked();
+        // logDispatchStateLocked();
 #endif
     } // release lock
 
@@ -3433,11 +3412,11 @@
                     getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
             if (oldFocusedWindowHandle != nullptr) {
                 sp<InputChannel> inputChannel =
-                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
+                        getInputChannelLocked(oldFocusedWindowHandle->getToken());
                 if (inputChannel != nullptr) {
-                    CancelationOptions options(
-                            CancelationOptions::CANCEL_NON_POINTER_EVENTS,
-                            "The display which contains this window no longer has focus.");
+                    CancelationOptions
+                            options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+                                    "The display which contains this window no longer has focus.");
                     options.displayId = ADISPLAY_ID_NONE;
                     synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
                 }
@@ -3456,8 +3435,8 @@
                     for (auto& it : mFocusedWindowHandlesByDisplay) {
                         const int32_t displayId = it.first;
                         const sp<InputWindowHandle>& windowHandle = it.second;
-                        ALOGE("Display #%" PRId32 " has focused window: '%s'\n",
-                                displayId, windowHandle->getName().c_str());
+                        ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
+                              windowHandle->getName().c_str());
                     }
                 }
             }
@@ -3547,7 +3526,7 @@
         }
 #if DEBUG_FOCUS
         ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-            fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
+              fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
 #endif
         if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
 #if DEBUG_FOCUS
@@ -3567,9 +3546,9 @@
 
                     state.windows.erase(state.windows.begin() + i);
 
-                    int32_t newTargetFlags = oldTargetFlags
-                            & (InputTarget::FLAG_FOREGROUND
-                                    | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+                    int32_t newTargetFlags = oldTargetFlags &
+                            (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+                             InputTarget::FLAG_DISPATCH_AS_IS);
                     state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
                     found = true;
@@ -3577,27 +3556,24 @@
                 }
             }
         }
-Found:
+    Found:
 
-        if (! found) {
+        if (!found) {
 #if DEBUG_FOCUS
             ALOGD("Focus transfer failed because from window did not have focus.");
 #endif
             return false;
         }
 
-
         sp<InputChannel> fromChannel = getInputChannelLocked(fromToken);
         sp<InputChannel> toChannel = getInputChannelLocked(toToken);
-        ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
-        ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
-        if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
-            sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
-            sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
-
+        sp<Connection> fromConnection = getConnectionLocked(fromChannel);
+        sp<Connection> toConnection = getConnectionLocked(toChannel);
+        if (fromConnection != nullptr && toConnection != nullptr) {
             fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
-            CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                    "transferring touch focus from this window to another window");
+            CancelationOptions
+                    options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                            "transferring touch focus from this window to another window");
             synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
         }
 
@@ -3652,12 +3628,12 @@
         for (auto& it : mFocusedApplicationHandlesByDisplay) {
             const int32_t displayId = it.first;
             const sp<InputApplicationHandle>& applicationHandle = it.second;
-            dump += StringPrintf(
-                    INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n",
-                    displayId,
-                    applicationHandle->getName().c_str(),
-                    applicationHandle->getDispatchingTimeout(
-                            DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32
+                                         ", name='%s', dispatchingTimeout=%0.3fms\n",
+                                 displayId, applicationHandle->getName().c_str(),
+                                 applicationHandle->getDispatchingTimeout(
+                                         DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
+                                         1000000.0);
         }
     } else {
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -3668,8 +3644,8 @@
         for (auto& it : mFocusedWindowHandlesByDisplay) {
             const int32_t displayId = it.first;
             const sp<InputWindowHandle>& windowHandle = it.second;
-            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n",
-                    displayId, windowHandle->getName().c_str());
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+                                 windowHandle->getName().c_str());
         }
     } else {
         dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
@@ -3680,16 +3656,16 @@
         for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
             const TouchState& state = mTouchStatesByDisplay.valueAt(i);
             dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
-                    state.displayId, toString(state.down), toString(state.split),
-                    state.deviceId, state.source);
+                                 state.displayId, toString(state.down), toString(state.split),
+                                 state.deviceId, state.source);
             if (!state.windows.empty()) {
                 dump += INDENT3 "Windows:\n";
                 for (size_t i = 0; i < state.windows.size(); i++) {
                     const TouchedWindow& touchedWindow = state.windows[i];
-                    dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
-                            i, touchedWindow.windowHandle->getName().c_str(),
-                            touchedWindow.pointerIds.value,
-                            touchedWindow.targetFlags);
+                    dump += StringPrintf(INDENT4
+                                         "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+                                         i, touchedWindow.windowHandle->getName().c_str(),
+                                         touchedWindow.pointerIds.value, touchedWindow.targetFlags);
                 }
             } else {
                 dump += INDENT3 "Windows: <none>\n";
@@ -3698,8 +3674,8 @@
                 dump += INDENT3 "Portal windows:\n";
                 for (size_t i = 0; i < state.portalWindows.size(); i++) {
                     const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i];
-                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
-                            i, portalWindowHandle->getName().c_str());
+                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
+                                         portalWindowHandle->getName().c_str());
                 }
             }
         }
@@ -3708,7 +3684,7 @@
     }
 
     if (!mWindowHandlesByDisplay.empty()) {
-       for (auto& it : mWindowHandlesByDisplay) {
+        for (auto& it : mWindowHandlesByDisplay) {
             const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
             dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
             if (!windowHandles.empty()) {
@@ -3718,28 +3694,31 @@
                     const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
-                            "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
-                            "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                            "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
-                            "touchableRegion=",
-                            i, windowInfo->name.c_str(), windowInfo->displayId,
-                            windowInfo->portalToDisplayId,
-                            toString(windowInfo->paused),
-                            toString(windowInfo->hasFocus),
-                            toString(windowInfo->hasWallpaper),
-                            toString(windowInfo->visible),
-                            toString(windowInfo->canReceiveKeys),
-                            windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
-                            windowInfo->layer,
-                            windowInfo->frameLeft, windowInfo->frameTop,
-                            windowInfo->frameRight, windowInfo->frameBottom,
-                            windowInfo->globalScaleFactor,
-                            windowInfo->windowXScale, windowInfo->windowYScale);
+                                                 "portalToDisplayId=%d, paused=%s, hasFocus=%s, "
+                                                 "hasWallpaper=%s, "
+                                                 "visible=%s, canReceiveKeys=%s, flags=0x%08x, "
+                                                 "type=0x%08x, layer=%d, "
+                                                 "frame=[%d,%d][%d,%d], globalScale=%f, "
+                                                 "windowScale=(%f,%f), "
+                                                 "touchableRegion=",
+                                         i, windowInfo->name.c_str(), windowInfo->displayId,
+                                         windowInfo->portalToDisplayId,
+                                         toString(windowInfo->paused),
+                                         toString(windowInfo->hasFocus),
+                                         toString(windowInfo->hasWallpaper),
+                                         toString(windowInfo->visible),
+                                         toString(windowInfo->canReceiveKeys),
+                                         windowInfo->layoutParamsFlags,
+                                         windowInfo->layoutParamsType, windowInfo->layer,
+                                         windowInfo->frameLeft, windowInfo->frameTop,
+                                         windowInfo->frameRight, windowInfo->frameBottom,
+                                         windowInfo->globalScaleFactor, windowInfo->windowXScale,
+                                         windowInfo->windowYScale);
                     dumpRegion(dump, windowInfo->touchableRegion);
                     dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                            windowInfo->ownerPid, windowInfo->ownerUid,
-                            windowInfo->dispatchingTimeout / 1000000.0);
+                                         windowInfo->ownerPid, windowInfo->ownerUid,
+                                         windowInfo->dispatchingTimeout / 1000000.0);
                 }
             } else {
                 dump += INDENT2 "Windows: <none>\n";
@@ -3750,16 +3729,16 @@
     }
 
     if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) {
-       for (auto& it : mGlobalMonitorsByDisplay) {
+        for (auto& it : mGlobalMonitorsByDisplay) {
             const std::vector<Monitor>& monitors = it.second;
             dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first);
             dumpMonitors(dump, monitors);
-       }
-       for (auto& it : mGestureMonitorsByDisplay) {
+        }
+        for (auto& it : mGestureMonitorsByDisplay) {
             const std::vector<Monitor>& monitors = it.second;
             dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first);
             dumpMonitors(dump, monitors);
-       }
+        }
     } else {
         dump += INDENT "Monitors: <none>\n";
     }
@@ -3772,8 +3751,7 @@
         for (EventEntry* entry : mRecentQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n",
-                    (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
         }
     } else {
         dump += INDENT "RecentQueue: <empty>\n";
@@ -3785,7 +3763,7 @@
         dump += INDENT2;
         mPendingEvent->appendDescription(dump);
         dump += StringPrintf(", age=%0.1fms\n",
-                (currentTime - mPendingEvent->eventTime) * 0.000001f);
+                             (currentTime - mPendingEvent->eventTime) * 0.000001f);
     } else {
         dump += INDENT "PendingEvent: <none>\n";
     }
@@ -3796,8 +3774,7 @@
         for (EventEntry* entry : mInboundQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n",
-                    (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
         }
     } else {
         dump += INDENT "InboundQueue: <empty>\n";
@@ -3808,23 +3785,23 @@
         for (size_t i = 0; i < mReplacedKeys.size(); i++) {
             const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
             int32_t newKeyCode = mReplacedKeys.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n",
-                    i, replacement.keyCode, replacement.deviceId, newKeyCode);
+            dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+                                 replacement.keyCode, replacement.deviceId, newKeyCode);
         }
     } else {
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
-    if (!mConnectionsByFd.isEmpty()) {
+    if (!mConnectionsByFd.empty()) {
         dump += INDENT "Connections:\n";
-        for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-            const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', "
-                    "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
-                    i, connection->getInputChannelName().c_str(),
-                    connection->getWindowName().c_str(),
-                    connection->getStatusLabel(), toString(connection->monitor),
-                    toString(connection->inputPublisherBlocked));
+        for (const auto& pair : mConnectionsByFd) {
+            const sp<Connection>& connection = pair.second;
+            dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
+                                         "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
+                                 pair.first, connection->getInputChannelName().c_str(),
+                                 connection->getWindowName().c_str(), connection->getStatusLabel(),
+                                 toString(connection->monitor),
+                                 toString(connection->inputPublisherBlocked));
 
             if (!connection->outboundQueue.empty()) {
                 dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
@@ -3833,8 +3810,8 @@
                     dump.append(INDENT4);
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
-                            entry->targetFlags, entry->resolvedAction,
-                            (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+                                         entry->targetFlags, entry->resolvedAction,
+                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f);
                 }
             } else {
                 dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -3847,10 +3824,10 @@
                     dump += INDENT4;
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
-                            "age=%0.1fms, wait=%0.1fms\n",
-                            entry->targetFlags, entry->resolvedAction,
-                            (currentTime - entry->eventEntry->eventTime) * 0.000001f,
-                            (currentTime - entry->deliveryTime) * 0.000001f);
+                                         "age=%0.1fms, wait=%0.1fms\n",
+                                         entry->targetFlags, entry->resolvedAction,
+                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f,
+                                         (currentTime - entry->deliveryTime) * 0.000001f);
                 }
             } else {
                 dump += INDENT3 "WaitQueue: <empty>\n";
@@ -3862,16 +3839,15 @@
 
     if (isAppSwitchPendingLocked()) {
         dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
-                (mAppSwitchDueTime - now()) / 1000000.0);
+                             (mAppSwitchDueTime - now()) / 1000000.0);
     } else {
         dump += INDENT "AppSwitch: not pending\n";
     }
 
     dump += INDENT "Configuration:\n";
-    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n",
-            mConfig.keyRepeatDelay * 0.000001f);
+    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
     dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
-            mConfig.keyRepeatTimeout * 0.000001f);
+                         mConfig.keyRepeatTimeout * 0.000001f);
 }
 
 void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -3885,25 +3861,25 @@
 }
 
 status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
-        int32_t displayId) {
+                                               int32_t displayId) {
 #if DEBUG_REGISTRATION
     ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
-            inputChannel->getName().c_str(), displayId);
+          inputChannel->getName().c_str(), displayId);
 #endif
 
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        if (getConnectionIndexLocked(inputChannel) >= 0) {
+        sp<Connection> existingConnection = getConnectionLocked(inputChannel);
+        if (existingConnection != nullptr) {
             ALOGW("Attempted to register already registered input channel '%s'",
-                    inputChannel->getName().c_str());
+                  inputChannel->getName().c_str());
             return BAD_VALUE;
         }
 
         sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
 
         int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
+        mConnectionsByFd[fd] = connection;
         mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
@@ -3915,7 +3891,7 @@
 }
 
 status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
-        int32_t displayId, bool isGestureMonitor) {
+                                               int32_t displayId, bool isGestureMonitor) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3932,16 +3908,14 @@
         sp<Connection> connection = new Connection(inputChannel, true /*monitor*/);
 
         const int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
+        mConnectionsByFd[fd] = connection;
         mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
 
-        auto& monitorsByDisplay = isGestureMonitor
-                ? mGestureMonitorsByDisplay
-                : mGlobalMonitorsByDisplay;
+        auto& monitorsByDisplay =
+                isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
         monitorsByDisplay[displayId].emplace_back(inputChannel);
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
-
     }
     // Wake the looper because some connections have changed.
     mLooper->wake();
@@ -3969,17 +3943,16 @@
 }
 
 status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
-        bool notify) {
-    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-    if (connectionIndex < 0) {
+                                                       bool notify) {
+    sp<Connection> connection = getConnectionLocked(inputChannel);
+    if (connection == nullptr) {
         ALOGW("Attempted to unregister already unregistered input channel '%s'",
-                inputChannel->getName().c_str());
+              inputChannel->getName().c_str());
         return BAD_VALUE;
     }
 
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-    mConnectionsByFd.removeItemsAt(connectionIndex);
-
+    const bool removed = removeByValue(mConnectionsByFd, connection);
+    ALOG_ASSERT(removed);
     mInputChannelsByToken.erase(inputChannel->getToken());
 
     if (connection->monitor) {
@@ -4000,16 +3973,17 @@
     removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
 }
 
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+void InputDispatcher::removeMonitorChannelLocked(
+        const sp<InputChannel>& inputChannel,
         std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
-    for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) {
+    for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
         std::vector<Monitor>& monitors = it->second;
         const size_t numMonitors = monitors.size();
         for (size_t i = 0; i < numMonitors; i++) {
-             if (monitors[i].inputChannel == inputChannel) {
-                 monitors.erase(monitors.begin() + i);
-                 break;
-             }
+            if (monitors[i].inputChannel == inputChannel) {
+                monitors.erase(monitors.begin() + i);
+                break;
+            }
         }
         if (monitors.empty()) {
             it = monitorsByDisplay.erase(it);
@@ -4045,14 +4019,14 @@
         }
         if (!foundDeviceId || !state.down) {
             ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
-                    " Ignoring.");
+                  " Ignoring.");
             return BAD_VALUE;
         }
         int32_t deviceId = foundDeviceId.value();
 
         // Send cancel events to all the input channels we're stealing from.
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                "gesture monitor stole pointer stream");
+                                   "gesture monitor stole pointer stream");
         options.deviceId = deviceId;
         options.displayId = displayId;
         for (const TouchedWindow& window : state.windows) {
@@ -4065,7 +4039,6 @@
     return OK;
 }
 
-
 std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
         const sp<IBinder>& token) {
     for (const auto& it : mGestureMonitorsByDisplay) {
@@ -4079,23 +4052,24 @@
     return std::nullopt;
 }
 
-ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<InputChannel>& inputChannel) {
     if (inputChannel == nullptr) {
-        return -1;
+        return nullptr;
     }
 
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        sp<Connection> connection = mConnectionsByFd.valueAt(i);
+    for (const auto& pair : mConnectionsByFd) {
+        sp<Connection> connection = pair.second;
         if (connection->inputChannel->getToken() == inputChannel->getToken()) {
-            return i;
+            return connection;
         }
     }
 
-    return -1;
+    return nullptr;
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(
-        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
+void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
+                                                    const sp<Connection>& connection, uint32_t seq,
+                                                    bool handled) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
@@ -4105,10 +4079,10 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(
-        nsecs_t currentTime, const sp<Connection>& connection) {
+void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
+                                                  const sp<Connection>& connection) {
     ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
-            connection->getInputChannelName().c_str());
+          connection->getInputChannelName().c_str());
 
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
@@ -4117,7 +4091,7 @@
 }
 
 void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-        const sp<InputWindowHandle>& newFocus) {
+                                           const sp<InputWindowHandle>& newFocus) {
     sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
     sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
@@ -4127,16 +4101,16 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onANRLocked(
-        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
-        const sp<InputWindowHandle>& windowHandle,
-        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
+void InputDispatcher::onANRLocked(nsecs_t currentTime,
+                                  const sp<InputApplicationHandle>& applicationHandle,
+                                  const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
+                                  nsecs_t waitStartTime, const char* reason) {
     float dispatchLatency = (currentTime - eventTime) * 0.000001f;
     float waitDuration = (currentTime - waitStartTime) * 0.000001f;
     ALOGI("Application is not responding: %s.  "
-            "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
-            getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
-            dispatchLatency, waitDuration, reason);
+          "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
+          getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
+          waitDuration, reason);
 
     // Capture a record of the InputDispatcher state at the time of the ANR.
     time_t t = time(nullptr);
@@ -4147,8 +4121,9 @@
     mLastANRState.clear();
     mLastANRState += INDENT "ANR:\n";
     mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
-    mLastANRState += StringPrintf(INDENT2 "Window: %s\n",
-            getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
+    mLastANRState +=
+            StringPrintf(INDENT2 "Window: %s\n",
+                         getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
     mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
     mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
     mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
@@ -4157,14 +4132,13 @@
     std::unique_ptr<CommandEntry> commandEntry =
             std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
-    commandEntry->inputChannel = windowHandle != nullptr ?
-            getInputChannelLocked(windowHandle->getToken()) : nullptr;
+    commandEntry->inputChannel =
+            windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
     commandEntry->reason = reason;
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible (
-        CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
     mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
@@ -4172,8 +4146,7 @@
     mLock.lock();
 }
 
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
-        CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
 
     if (connection->status != Connection::STATUS_ZOMBIE) {
@@ -4185,8 +4158,7 @@
     }
 }
 
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
-        CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
     sp<IBinder> oldToken = commandEntry->oldToken;
     sp<IBinder> newToken = commandEntry->newToken;
     mLock.unlock();
@@ -4194,19 +4166,18 @@
     mLock.lock();
 }
 
-void InputDispatcher::doNotifyANRLockedInterruptible(
-        CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    nsecs_t newTimeout = mPolicy->notifyANR(
-            commandEntry->inputApplicationHandle,
-            commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
-            commandEntry->reason);
+    nsecs_t newTimeout =
+            mPolicy->notifyANR(commandEntry->inputApplicationHandle,
+                               commandEntry->inputChannel ? commandEntry->inputChannel->getToken()
+                                                          : nullptr,
+                               commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
-            commandEntry->inputChannel);
+    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4219,13 +4190,13 @@
     mLock.unlock();
 
     android::base::Timer t;
-    sp<IBinder> token = commandEntry->inputChannel != nullptr ?
-        commandEntry->inputChannel->getToken() : nullptr;
-    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
-            &event, entry->policyFlags);
+    sp<IBinder> token = commandEntry->inputChannel != nullptr
+            ? commandEntry->inputChannel->getToken()
+            : nullptr;
+    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
-                std::to_string(t.duration().count()).c_str());
+              std::to_string(t.duration().count()).c_str());
     }
 
     mLock.lock();
@@ -4247,16 +4218,14 @@
     mLock.lock();
 }
 
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
-        CommandEntry* commandEntry) {
+void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
     const nsecs_t finishTime = commandEntry->eventTime;
     uint32_t seq = commandEntry->seq;
     const bool handled = commandEntry->handled;
 
     // Handle post-event policy actions.
-    std::deque<InputDispatcher::DispatchEntry*>::iterator dispatchEntryIt =
-            connection->findWaitQueueEntry(seq);
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
     if (dispatchEntryIt == connection->waitQueue.end()) {
         return;
     }
@@ -4306,7 +4275,8 @@
 }
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
-        DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) {
+                                                       DispatchEntry* dispatchEntry,
+                                                       KeyEntry* keyEntry, bool handled) {
     if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
@@ -4331,9 +4301,9 @@
             // Dispatch the unhandled key to the policy with the cancel flag.
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
-                    "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                    keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
-                    keyEntry->policyFlags);
+                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                  keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+                  keyEntry->policyFlags);
 #endif
             KeyEvent event;
             initializeKeyEvent(&event, keyEntry);
@@ -4341,8 +4311,8 @@
 
             mLock.unlock();
 
-            mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
-                                          &event, keyEntry->policyFlags, &event);
+            mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+                                          keyEntry->policyFlags, &event);
 
             mLock.lock();
 
@@ -4361,15 +4331,13 @@
         // If the application did not handle a non-fallback key, first check
         // that we are in a good state to perform unhandled key event processing
         // Then ask the policy what to do with it.
-        bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
-                && keyEntry->repeatCount == 0;
+        bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
         if (fallbackKeyCode == -1 && !initialDown) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Skipping unhandled key event processing "
-                    "since this is not an initial down.  "
-                    "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                    originalKeyCode, keyEntry->action, keyEntry->repeatCount,
-                    keyEntry->policyFlags);
+                  "since this is not an initial down.  "
+                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                  originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
 #endif
             return false;
         }
@@ -4377,17 +4345,16 @@
         // Dispatch the unhandled key to the policy.
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
-                "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
-                keyEntry->policyFlags);
+              "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+              keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
 #endif
         KeyEvent event;
         initializeKeyEvent(&event, keyEntry);
 
         mLock.unlock();
 
-        bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
-                                                      &event, keyEntry->policyFlags, &event);
+        bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+                                                      keyEntry->policyFlags, &event);
 
         mLock.lock();
 
@@ -4412,19 +4379,19 @@
         // Cancel the fallback key if the policy decides not to send it anymore.
         // We will continue to dispatch the key to the policy but we will no
         // longer dispatch a fallback key to the application.
-        if (fallbackKeyCode != AKEYCODE_UNKNOWN
-                && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+        if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
+            (!fallback || fallbackKeyCode != event.getKeyCode())) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             if (fallback) {
                 ALOGD("Unhandled key event: Policy requested to send key %d"
-                        "as a fallback for %d, but on the DOWN it had requested "
-                        "to send %d instead.  Fallback canceled.",
-                        event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+                      "as a fallback for %d, but on the DOWN it had requested "
+                      "to send %d instead.  Fallback canceled.",
+                      event.getKeyCode(), originalKeyCode, fallbackKeyCode);
             } else {
                 ALOGD("Unhandled key event: Policy did not request fallback for %d, "
-                        "but on the DOWN it had requested to send %d.  "
-                        "Fallback canceled.",
-                        originalKeyCode, fallbackKeyCode);
+                      "but on the DOWN it had requested to send %d.  "
+                      "Fallback canceled.",
+                      originalKeyCode, fallbackKeyCode);
             }
 #endif
 
@@ -4436,8 +4403,7 @@
             fallback = false;
             fallbackKeyCode = AKEYCODE_UNKNOWN;
             if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
-                connection->inputState.setFallbackKey(originalKeyCode,
-                                                      fallbackKeyCode);
+                connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
             }
         }
 
@@ -4447,11 +4413,10 @@
             const KeyedVector<int32_t, int32_t>& fallbackKeys =
                     connection->inputState.getFallbackKeys();
             for (size_t i = 0; i < fallbackKeys.size(); i++) {
-                msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
-                        fallbackKeys.valueAt(i));
+                msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
             }
             ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
-                    fallbackKeys.size(), msg.c_str());
+                  fallbackKeys.size(), msg.c_str());
         }
 #endif
 
@@ -4471,8 +4436,8 @@
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Unhandled key event: Dispatching fallback key.  "
-                    "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                    originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+                  "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+                  originalKeyCode, fallbackKeyCode, keyEntry->metaState);
 #endif
             return true; // restart the event
         } else {
@@ -4488,7 +4453,8 @@
 }
 
 bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
-        DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) {
+                                                          DispatchEntry* dispatchEntry,
+                                                          MotionEntry* motionEntry, bool handled) {
     return false;
 }
 
@@ -4502,12 +4468,13 @@
 
 void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
     event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
-            entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
-            entry->downTime, entry->eventTime);
+                      entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+                      entry->downTime, entry->eventTime);
 }
 
 void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
-        int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+                                               int32_t injectionResult,
+                                               nsecs_t timeSpentWaitingForApplication) {
     // TODO Write some statistics about how long we spend waiting.
 }
 
@@ -4552,800 +4519,4 @@
     mDispatcherIsAlive.wait(_l);
 }
 
-
-// --- InputDispatcher::InjectionState ---
-
-InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) :
-        refCount(1),
-        injectorPid(injectorPid), injectorUid(injectorUid),
-        injectionResult(INPUT_EVENT_INJECTION_PENDING), injectionIsAsync(false),
-        pendingForegroundDispatches(0) {
-}
-
-InputDispatcher::InjectionState::~InjectionState() {
-}
-
-void InputDispatcher::InjectionState::release() {
-    refCount -= 1;
-    if (refCount == 0) {
-        delete this;
-    } else {
-        ALOG_ASSERT(refCount > 0);
-    }
-}
-
-
-// --- InputDispatcher::EventEntry ---
-
-InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type,
-        nsecs_t eventTime, uint32_t policyFlags) :
-        sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime),
-        policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) {
-}
-
-InputDispatcher::EventEntry::~EventEntry() {
-    releaseInjectionState();
-}
-
-void InputDispatcher::EventEntry::release() {
-    refCount -= 1;
-    if (refCount == 0) {
-        delete this;
-    } else {
-        ALOG_ASSERT(refCount > 0);
-    }
-}
-
-void InputDispatcher::EventEntry::releaseInjectionState() {
-    if (injectionState) {
-        injectionState->release();
-        injectionState = nullptr;
-    }
-}
-
-
-// --- InputDispatcher::ConfigurationChangedEntry ---
-
-InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(
-        uint32_t sequenceNum, nsecs_t eventTime) :
-        EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
-}
-
-InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {
-}
-
-void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
-}
-
-
-// --- InputDispatcher::DeviceResetEntry ---
-
-InputDispatcher::DeviceResetEntry::DeviceResetEntry(
-        uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
-        EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0),
-        deviceId(deviceId) {
-}
-
-InputDispatcher::DeviceResetEntry::~DeviceResetEntry() {
-}
-
-void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x",
-            deviceId, policyFlags);
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, 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,
-        int32_t repeatCount, nsecs_t downTime) :
-        EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
-        deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags),
-        keyCode(keyCode), scanCode(scanCode), metaState(metaState),
-        repeatCount(repeatCount), downTime(downTime),
-        syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
-        interceptKeyWakeupTime(0) {
-}
-
-InputDispatcher::KeyEntry::~KeyEntry() {
-}
-
-void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
-            "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
-            "repeatCount=%d), policyFlags=0x%08x",
-            deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode,
-            scanCode, metaState, repeatCount, policyFlags);
-}
-
-void InputDispatcher::KeyEntry::recycle() {
-    releaseInjectionState();
-
-    dispatchInProgress = false;
-    syntheticRepeat = false;
-    interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
-    interceptKeyWakeupTime = 0;
-}
-
-
-// --- InputDispatcher::MotionEntry ---
-
-InputDispatcher::MotionEntry::MotionEntry(
-        uint32_t sequenceNum, 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, float xPrecision, float yPrecision, float xCursorPosition,
-        float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
-        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-        float xOffset, float yOffset)
-      : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
-        eventTime(eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        action(action),
-        actionButton(actionButton),
-        flags(flags),
-        metaState(metaState),
-        buttonState(buttonState),
-        classification(classification),
-        edgeFlags(edgeFlags),
-        xPrecision(xPrecision),
-        yPrecision(yPrecision),
-        xCursorPosition(xCursorPosition),
-        yCursorPosition(yCursorPosition),
-        downTime(downTime),
-        pointerCount(pointerCount) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties[i].copyFrom(pointerProperties[i]);
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
-        if (xOffset || yOffset) {
-            this->pointerCoords[i].applyOffset(xOffset, yOffset);
-        }
-    }
-}
-
-InputDispatcher::MotionEntry::~MotionEntry() {
-}
-
-void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
-                        ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
-                        "buttonState=0x%08x, "
-                        "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
-                        "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
-                        deviceId, source, displayId, motionActionToString(action).c_str(),
-                        actionButton, flags, metaState, buttonState,
-                        motionClassificationToString(classification), edgeFlags, xPrecision,
-                        yPrecision, xCursorPosition, yCursorPosition);
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        if (i) {
-            msg += ", ";
-        }
-        msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id,
-                pointerCoords[i].getX(), pointerCoords[i].getY());
-    }
-    msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
-}
-
-
-// --- InputDispatcher::DispatchEntry ---
-
-volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
-
-InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
-        int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
-        float windowXScale, float windowYScale) :
-        seq(nextSeq()),
-        eventEntry(eventEntry), targetFlags(targetFlags),
-        xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
-        windowXScale(windowXScale), windowYScale(windowYScale),
-        deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
-    eventEntry->refCount += 1;
-}
-
-InputDispatcher::DispatchEntry::~DispatchEntry() {
-    eventEntry->release();
-}
-
-uint32_t InputDispatcher::DispatchEntry::nextSeq() {
-    // Sequence number 0 is reserved and will never be returned.
-    uint32_t seq;
-    do {
-        seq = android_atomic_inc(&sNextSeqAtomic);
-    } while (!seq);
-    return seq;
-}
-
-
-// --- InputDispatcher::InputState ---
-
-InputDispatcher::InputState::InputState() {
-}
-
-InputDispatcher::InputState::~InputState() {
-}
-
-bool InputDispatcher::InputState::isNeutral() const {
-    return mKeyMementos.empty() && mMotionMementos.empty();
-}
-
-bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source,
-        int32_t displayId) const {
-    for (const MotionMemento& memento : mMotionMementos) {
-        if (memento.deviceId == deviceId
-                && memento.source == source
-                && memento.displayId == displayId
-                && memento.hovering) {
-            return true;
-        }
-    }
-    return false;
-}
-
-bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
-        int32_t action, int32_t flags) {
-    switch (action) {
-    case AKEY_EVENT_ACTION_UP: {
-        if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
-            for (size_t i = 0; i < mFallbackKeys.size(); ) {
-                if (mFallbackKeys.valueAt(i) == entry->keyCode) {
-                    mFallbackKeys.removeItemsAt(i);
-                } else {
-                    i += 1;
-                }
-            }
-        }
-        ssize_t index = findKeyMemento(entry);
-        if (index >= 0) {
-            mKeyMementos.erase(mKeyMementos.begin() + index);
-            return true;
-        }
-        /* FIXME: We can't just drop the key up event because that prevents creating
-         * popup windows that are automatically shown when a key is held and then
-         * dismissed when the key is released.  The problem is that the popup will
-         * not have received the original key down, so the key up will be considered
-         * to be inconsistent with its observed state.  We could perhaps handle this
-         * by synthesizing a key down but that will cause other problems.
-         *
-         * So for now, allow inconsistent key up events to be dispatched.
-         *
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
-                "keyCode=%d, scanCode=%d",
-                entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
-#endif
-        return false;
-        */
-        return true;
-    }
-
-    case AKEY_EVENT_ACTION_DOWN: {
-        ssize_t index = findKeyMemento(entry);
-        if (index >= 0) {
-            mKeyMementos.erase(mKeyMementos.begin() + index);
-        }
-        addKeyMemento(entry, flags);
-        return true;
-    }
-
-    default:
-        return true;
-    }
-}
-
-bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry,
-        int32_t action, int32_t flags) {
-    int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
-    switch (actionMasked) {
-    case AMOTION_EVENT_ACTION_UP:
-    case AMOTION_EVENT_ACTION_CANCEL: {
-        ssize_t index = findMotionMemento(entry, false /*hovering*/);
-        if (index >= 0) {
-            mMotionMementos.erase(mMotionMementos.begin() + index);
-            return true;
-        }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
-                "displayId=%" PRId32 ", actionMasked=%d",
-                entry->deviceId, entry->source, entry->displayId, actionMasked);
-#endif
-        return false;
-    }
-
-    case AMOTION_EVENT_ACTION_DOWN: {
-        ssize_t index = findMotionMemento(entry, false /*hovering*/);
-        if (index >= 0) {
-            mMotionMementos.erase(mMotionMementos.begin() + index);
-        }
-        addMotionMemento(entry, flags, false /*hovering*/);
-        return true;
-    }
-
-    case AMOTION_EVENT_ACTION_POINTER_UP:
-    case AMOTION_EVENT_ACTION_POINTER_DOWN:
-    case AMOTION_EVENT_ACTION_MOVE: {
-        if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
-            // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to
-            // generate cancellation events for these since they're based in relative rather than
-            // absolute units.
-            return true;
-        }
-
-        ssize_t index = findMotionMemento(entry, false /*hovering*/);
-
-        if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
-            // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
-            // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any
-            // other value and we need to track the motion so we can send cancellation events for
-            // anything generating fallback events (e.g. DPad keys for joystick movements).
-            if (index >= 0) {
-                if (entry->pointerCoords[0].isEmpty()) {
-                    mMotionMementos.erase(mMotionMementos.begin() + index);
-                } else {
-                    MotionMemento& memento = mMotionMementos[index];
-                    memento.setPointers(entry);
-                }
-            } else if (!entry->pointerCoords[0].isEmpty()) {
-                addMotionMemento(entry, flags, false /*hovering*/);
-            }
-
-            // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
-            return true;
-        }
-        if (index >= 0) {
-            MotionMemento& memento = mMotionMementos[index];
-            memento.setPointers(entry);
-            return true;
-        }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Dropping inconsistent motion pointer up/down or move event: "
-                "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
-                entry->deviceId, entry->source, entry->displayId, actionMasked);
-#endif
-        return false;
-    }
-
-    case AMOTION_EVENT_ACTION_HOVER_EXIT: {
-        ssize_t index = findMotionMemento(entry, true /*hovering*/);
-        if (index >= 0) {
-            mMotionMementos.erase(mMotionMementos.begin() + index);
-            return true;
-        }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
-                "displayId=%" PRId32,
-                entry->deviceId, entry->source, entry->displayId);
-#endif
-        return false;
-    }
-
-    case AMOTION_EVENT_ACTION_HOVER_ENTER:
-    case AMOTION_EVENT_ACTION_HOVER_MOVE: {
-        ssize_t index = findMotionMemento(entry, true /*hovering*/);
-        if (index >= 0) {
-            mMotionMementos.erase(mMotionMementos.begin() + index);
-        }
-        addMotionMemento(entry, flags, true /*hovering*/);
-        return true;
-    }
-
-    default:
-        return true;
-    }
-}
-
-ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
-    for (size_t i = 0; i < mKeyMementos.size(); i++) {
-        const KeyMemento& memento = mKeyMementos[i];
-        if (memento.deviceId == entry->deviceId
-                && memento.source == entry->source
-                && memento.displayId == entry->displayId
-                && memento.keyCode == entry->keyCode
-                && memento.scanCode == entry->scanCode) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
-        bool hovering) const {
-    for (size_t i = 0; i < mMotionMementos.size(); i++) {
-        const MotionMemento& memento = mMotionMementos[i];
-        if (memento.deviceId == entry->deviceId
-                && memento.source == entry->source
-                && memento.displayId == entry->displayId
-                && memento.hovering == hovering) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
-    KeyMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
-    memento.keyCode = entry->keyCode;
-    memento.scanCode = entry->scanCode;
-    memento.metaState = entry->metaState;
-    memento.flags = flags;
-    memento.downTime = entry->downTime;
-    memento.policyFlags = entry->policyFlags;
-    mKeyMementos.push_back(memento);
-}
-
-void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
-        int32_t flags, bool hovering) {
-    MotionMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
-    memento.flags = flags;
-    memento.xPrecision = entry->xPrecision;
-    memento.yPrecision = entry->yPrecision;
-    memento.xCursorPosition = entry->xCursorPosition;
-    memento.yCursorPosition = entry->yCursorPosition;
-    memento.downTime = entry->downTime;
-    memento.setPointers(entry);
-    memento.hovering = hovering;
-    memento.policyFlags = entry->policyFlags;
-    mMotionMementos.push_back(memento);
-}
-
-void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
-    pointerCount = entry->pointerCount;
-    for (uint32_t i = 0; i < entry->pointerCount; i++) {
-        pointerProperties[i].copyFrom(entry->pointerProperties[i]);
-        pointerCoords[i].copyFrom(entry->pointerCoords[i]);
-    }
-}
-
-void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
-        std::vector<EventEntry*>& outEvents, const CancelationOptions& options) {
-    for (KeyMemento& memento : mKeyMementos) {
-        if (shouldCancelKey(memento, options)) {
-            outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
-                    memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
-                    AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
-                    memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
-        }
-    }
-
-    for (const MotionMemento& memento : mMotionMementos) {
-        if (shouldCancelMotion(memento, options)) {
-            const int32_t action = memento.hovering ?
-                    AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
-            outEvents.push_back(
-                    new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
-                                    memento.source, memento.displayId, memento.policyFlags, action,
-                                    0 /*actionButton*/, memento.flags, AMETA_NONE,
-                                    0 /*buttonState*/, MotionClassification::NONE,
-                                    AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
-                                    memento.yPrecision, memento.xCursorPosition,
-                                    memento.yCursorPosition, memento.downTime, memento.pointerCount,
-                                    memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
-                                    0 /*yOffset*/));
-        }
-    }
-}
-
-void InputDispatcher::InputState::clear() {
-    mKeyMementos.clear();
-    mMotionMementos.clear();
-    mFallbackKeys.clear();
-}
-
-void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
-    for (size_t i = 0; i < mMotionMementos.size(); i++) {
-        const MotionMemento& memento = mMotionMementos[i];
-        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
-            for (size_t j = 0; j < other.mMotionMementos.size(); ) {
-                const MotionMemento& otherMemento = other.mMotionMementos[j];
-                if (memento.deviceId == otherMemento.deviceId
-                        && memento.source == otherMemento.source
-                        && memento.displayId == otherMemento.displayId) {
-                    other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
-                } else {
-                    j += 1;
-                }
-            }
-            other.mMotionMementos.push_back(memento);
-        }
-    }
-}
-
-int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) {
-    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
-    return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
-}
-
-void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode,
-        int32_t fallbackKeyCode) {
-    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
-    if (index >= 0) {
-        mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
-    } else {
-        mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
-    }
-}
-
-void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) {
-    mFallbackKeys.removeItem(originalKeyCode);
-}
-
-bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
-        const CancelationOptions& options) {
-    if (options.keyCode && memento.keyCode != options.keyCode.value()) {
-        return false;
-    }
-
-    if (options.deviceId  && memento.deviceId != options.deviceId.value()) {
-        return false;
-    }
-
-    if (options.displayId && memento.displayId != options.displayId.value()) {
-        return false;
-    }
-
-    switch (options.mode) {
-    case CancelationOptions::CANCEL_ALL_EVENTS:
-    case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
-        return true;
-    case CancelationOptions::CANCEL_FALLBACK_EVENTS:
-        return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
-    default:
-        return false;
-    }
-}
-
-bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
-        const CancelationOptions& options) {
-    if (options.deviceId && memento.deviceId != options.deviceId.value()) {
-        return false;
-    }
-
-    if (options.displayId && memento.displayId != options.displayId.value()) {
-        return false;
-    }
-
-    switch (options.mode) {
-    case CancelationOptions::CANCEL_ALL_EVENTS:
-        return true;
-    case CancelationOptions::CANCEL_POINTER_EVENTS:
-        return memento.source & AINPUT_SOURCE_CLASS_POINTER;
-    case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
-        return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
-    default:
-        return false;
-    }
-}
-
-
-// --- InputDispatcher::Connection ---
-
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) :
-        status(STATUS_NORMAL), inputChannel(inputChannel),
-        monitor(monitor),
-        inputPublisher(inputChannel), inputPublisherBlocked(false) {
-}
-
-InputDispatcher::Connection::~Connection() {
-}
-
-const std::string InputDispatcher::Connection::getWindowName() const {
-    if (inputChannel != nullptr) {
-        return inputChannel->getName();
-    }
-    if (monitor) {
-        return "monitor";
-    }
-    return "?";
-}
-
-const char* InputDispatcher::Connection::getStatusLabel() const {
-    switch (status) {
-    case STATUS_NORMAL:
-        return "NORMAL";
-
-    case STATUS_BROKEN:
-        return "BROKEN";
-
-    case STATUS_ZOMBIE:
-        return "ZOMBIE";
-
-    default:
-        return "UNKNOWN";
-    }
-}
-
-std::deque<InputDispatcher::DispatchEntry*>::iterator
-InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
-    for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
-        if ((*it)->seq == seq) {
-            return it;
-        }
-    }
-    return waitQueue.end();
-}
-
-// --- InputDispatcher::Monitor
-InputDispatcher::Monitor::Monitor(const sp<InputChannel>& inputChannel) :
-    inputChannel(inputChannel) {
-}
-
-
-// --- InputDispatcher::CommandEntry ---
-//
-InputDispatcher::CommandEntry::CommandEntry(Command command) :
-    command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0),
-    seq(0), handled(false) {
-}
-
-InputDispatcher::CommandEntry::~CommandEntry() {
-}
-
-// --- InputDispatcher::TouchedMonitor ---
-InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset,
-        float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {
-}
-
-// --- InputDispatcher::TouchState ---
-
-InputDispatcher::TouchState::TouchState() :
-    down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {
-}
-
-InputDispatcher::TouchState::~TouchState() {
-}
-
-void InputDispatcher::TouchState::reset() {
-    down = false;
-    split = false;
-    deviceId = -1;
-    source = 0;
-    displayId = ADISPLAY_ID_NONE;
-    windows.clear();
-    portalWindows.clear();
-    gestureMonitors.clear();
-}
-
-void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
-    down = other.down;
-    split = other.split;
-    deviceId = other.deviceId;
-    source = other.source;
-    displayId = other.displayId;
-    windows = other.windows;
-    portalWindows = other.portalWindows;
-    gestureMonitors = other.gestureMonitors;
-}
-
-void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
-        int32_t targetFlags, BitSet32 pointerIds) {
-    if (targetFlags & InputTarget::FLAG_SPLIT) {
-        split = true;
-    }
-
-    for (size_t i = 0; i < windows.size(); i++) {
-        TouchedWindow& touchedWindow = windows[i];
-        if (touchedWindow.windowHandle == windowHandle) {
-            touchedWindow.targetFlags |= targetFlags;
-            if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
-                touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
-            }
-            touchedWindow.pointerIds.value |= pointerIds.value;
-            return;
-        }
-    }
-
-    TouchedWindow touchedWindow;
-    touchedWindow.windowHandle = windowHandle;
-    touchedWindow.targetFlags = targetFlags;
-    touchedWindow.pointerIds = pointerIds;
-    windows.push_back(touchedWindow);
-}
-
-void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
-    size_t numWindows = portalWindows.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (portalWindows[i] == windowHandle) {
-            return;
-        }
-    }
-    portalWindows.push_back(windowHandle);
-}
-
-void InputDispatcher::TouchState::addGestureMonitors(
-        const std::vector<TouchedMonitor>& newMonitors) {
-    const size_t newSize = gestureMonitors.size() + newMonitors.size();
-    gestureMonitors.reserve(newSize);
-    gestureMonitors.insert(std::end(gestureMonitors),
-            std::begin(newMonitors), std::end(newMonitors));
-}
-
-void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
-    for (size_t i = 0; i < windows.size(); i++) {
-        if (windows[i].windowHandle == windowHandle) {
-            windows.erase(windows.begin() + i);
-            return;
-        }
-    }
-}
-
-void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
-    for (size_t i = 0; i < windows.size(); i++) {
-        if (windows[i].windowHandle->getToken() == token) {
-            windows.erase(windows.begin() + i);
-            return;
-        }
-    }
-}
-
-void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
-    for (size_t i = 0 ; i < windows.size(); ) {
-        TouchedWindow& window = windows[i];
-        if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
-                | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
-            window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
-            window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
-            i += 1;
-        } else {
-            windows.erase(windows.begin() + i);
-        }
-    }
-}
-
-void InputDispatcher::TouchState::filterNonMonitors() {
-    windows.clear();
-    portalWindows.clear();
-}
-
-sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const {
-    for (size_t i = 0; i < windows.size(); i++) {
-        const TouchedWindow& window = windows[i];
-        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            return window.windowHandle;
-        }
-    }
-    return nullptr;
-}
-
-bool InputDispatcher::TouchState::isSlippery() const {
-    // Must have exactly one foreground window.
-    bool haveSlipperyForegroundWindow = false;
-    for (const TouchedWindow& window : windows) {
-        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            if (haveSlipperyForegroundWindow
-                    || !(window.windowHandle->getInfo()->layoutParamsFlags
-                            & InputWindowInfo::FLAG_SLIPPERY)) {
-                return false;
-            }
-            haveSlipperyForegroundWindow = true;
-        }
-    }
-    return haveSlipperyForegroundWindow;
-}
-
-
-// --- InputDispatcherThread ---
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
-        Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
-}
-
-InputDispatcherThread::~InputDispatcherThread() {
-}
-
-bool InputDispatcherThread::threadLoop() {
-    mDispatcher->dispatchOnce();
-    return true;
-}
-
-} // namespace android
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
new file mode 100644
index 0000000..f2c0402
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_DISPATCHER_H
+#define _UI_INPUT_DISPATCHER_H
+
+#include "CancelationOptions.h"
+#include "Entry.h"
+#include "InjectionState.h"
+#include "InputDispatcherConfiguration.h"
+#include "InputDispatcherInterface.h"
+#include "InputDispatcherPolicyInterface.h"
+#include "InputState.h"
+#include "InputTarget.h"
+#include "Monitor.h"
+#include "TouchState.h"
+#include "TouchedWindow.h"
+
+#include <input/Input.h>
+#include <input/InputApplication.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+#include <limits.h>
+#include <stddef.h>
+#include <ui/Region.h>
+#include <unistd.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+#include <condition_variable>
+#include <deque>
+#include <optional>
+#include <unordered_map>
+
+#include <InputListener.h>
+#include <InputReporterInterface.h>
+
+namespace android::inputdispatcher {
+
+class Connection;
+
+/* Dispatches events to input targets.  Some functions of the input dispatcher, such as
+ * identifying input targets, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ *     Because the policy can potentially block or cause re-entrance into the input dispatcher,
+ *     the input dispatcher never calls into the policy while holding its internal locks.
+ *     The implementation is also carefully designed to recover from scenarios such as an
+ *     input channel becoming unregistered while identifying input targets or processing timeouts.
+ *
+ *     Methods marked 'Locked' must be called with the lock acquired.
+ *
+ *     Methods marked 'LockedInterruptible' must be called with the lock acquired but
+ *     may during the course of their execution release the lock, call into the policy, and
+ *     then reacquire the lock.  The caller is responsible for recovering gracefully.
+ *
+ *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
+ */
+class InputDispatcher : public android::InputDispatcherInterface {
+protected:
+    virtual ~InputDispatcher();
+
+public:
+    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
+
+    virtual void dispatchOnce() override;
+
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+    virtual void notifyKey(const NotifyKeyArgs* args) override;
+    virtual void notifyMotion(const NotifyMotionArgs* args) override;
+    virtual void notifySwitch(const NotifySwitchArgs* args) override;
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+    virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
+                                     int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+                                     uint32_t policyFlags) override;
+
+    virtual void setInputWindows(
+            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
+            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
+    virtual void setFocusedApplication(
+            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
+    virtual void setFocusedDisplay(int32_t displayId) override;
+    virtual void setInputDispatchMode(bool enabled, bool frozen) override;
+    virtual void setInputFilterEnabled(bool enabled) override;
+
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
+                                    const sp<IBinder>& toToken) override;
+
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
+                                          int32_t displayId) override;
+    virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
+                                          bool isGestureMonitor) override;
+    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+    virtual status_t pilferPointers(const sp<IBinder>& token) override;
+
+private:
+
+    enum DropReason {
+        DROP_REASON_NOT_DROPPED = 0,
+        DROP_REASON_POLICY = 1,
+        DROP_REASON_APP_SWITCH = 2,
+        DROP_REASON_DISABLED = 3,
+        DROP_REASON_BLOCKED = 4,
+        DROP_REASON_STALE = 5,
+    };
+
+    sp<InputDispatcherPolicyInterface> mPolicy;
+    android::InputDispatcherConfiguration mConfig;
+
+    std::mutex mLock;
+
+    std::condition_variable mDispatcherIsAlive;
+
+    sp<Looper> mLooper;
+
+    EventEntry* mPendingEvent GUARDED_BY(mLock);
+    std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
+    std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+    DropReason mLastDropReason GUARDED_BY(mLock);
+
+    void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
+
+    // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
+    bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+    // Cleans up input state when dropping an inbound event.
+    void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
+
+    // Adds an event to a queue of recent events for debugging purposes.
+    void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+    // App switch latency optimization.
+    bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
+    nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
+
+    bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+    bool isAppSwitchPendingLocked() REQUIRES(mLock);
+    void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
+
+    // Stale event latency optimization.
+    static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
+
+    // Blocked event latency optimization.  Drops old events when the user intends
+    // to transfer focus to a new application.
+    EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
+
+    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+                                                    bool addOutsideTargets = false,
+                                                    bool addPortalWindows = false) REQUIRES(mLock);
+
+    // All registered connections mapped by channel file descriptor.
+    std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& b) const {
+            return std::hash<IBinder*>{}(b.get());
+        }
+    };
+    std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
+            GUARDED_BY(mLock);
+
+    // Finds the display ID of the gesture monitor identified by the provided token.
+    std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
+            REQUIRES(mLock);
+
+    sp<Connection> getConnectionLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+
+    // Input channels that will receive a copy of all input events sent to the provided display.
+    std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
+
+    // Input channels that will receive pointer events that start within the corresponding display.
+    // These are a bit special when compared to global monitors since they'll cause gesture streams
+    // to continue even when there isn't a touched window,and have the ability to steal the rest of
+    // the pointer stream in order to claim it for a system gesture.
+    std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock);
+
+    // Event injection and synchronization.
+    std::condition_variable mInjectionResultAvailable;
+    bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
+    void setInjectionResult(EventEntry* entry, int32_t injectionResult);
+
+    std::condition_variable mInjectionSyncFinished;
+    void incrementPendingForegroundDispatches(EventEntry* entry);
+    void decrementPendingForegroundDispatches(EventEntry* entry);
+
+    // Key repeat tracking.
+    struct KeyRepeatState {
+        KeyEntry* lastKeyEntry; // or null if no repeat
+        nsecs_t nextRepeatTime;
+    } mKeyRepeatState GUARDED_BY(mLock);
+
+    void resetKeyRepeatLocked() REQUIRES(mLock);
+    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
+
+    // Key replacement tracking
+    struct KeyReplacement {
+        int32_t keyCode;
+        int32_t deviceId;
+        bool operator==(const KeyReplacement& rhs) const {
+            return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
+        }
+        bool operator<(const KeyReplacement& rhs) const {
+            return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+        }
+    };
+    // Maps the key code replaced, device id tuple to the key code it was replaced with
+    KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+    // Process certain Meta + Key combinations
+    void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
+                                 int32_t& metaState);
+
+    // Deferred command processing.
+    bool haveCommandsLocked() const REQUIRES(mLock);
+    bool runCommandsLockedInterruptible() REQUIRES(mLock);
+    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+
+    // Input filter processing.
+    bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
+    bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+
+    // Inbound event processing.
+    void drainInboundQueueLocked() REQUIRES(mLock);
+    void releasePendingEventLocked() REQUIRES(mLock);
+    void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+
+    // Dispatch state.
+    bool mDispatchEnabled GUARDED_BY(mLock);
+    bool mDispatchFrozen GUARDED_BY(mLock);
+    bool mInputFilterEnabled GUARDED_BY(mLock);
+
+    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
+            GUARDED_BY(mLock);
+    // Get window handles by display, return an empty vector if not found.
+    std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+            REQUIRES(mLock);
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
+            REQUIRES(mLock);
+    sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+    bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+
+    /*
+     * Validate and update InputWindowHandles for a given display.
+     */
+    void updateWindowHandlesForDisplayLocked(
+            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
+            REQUIRES(mLock);
+
+    // Focus tracking for keys, trackball, etc.
+    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
+            GUARDED_BY(mLock);
+
+    KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+    TouchState mTempTouchState GUARDED_BY(mLock);
+
+    // Focused applications.
+    std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
+            GUARDED_BY(mLock);
+
+    // Top focused display.
+    int32_t mFocusedDisplayId GUARDED_BY(mLock);
+
+    // Dispatcher state at time of last ANR.
+    std::string mLastANRState GUARDED_BY(mLock);
+
+    // Dispatch inbound events.
+    bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
+            REQUIRES(mLock);
+    bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
+    bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason,
+                           nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
+                              nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
+                             const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+
+    void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
+    void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
+
+    // Keeping track of ANR timeouts.
+    enum InputTargetWaitCause {
+        INPUT_TARGET_WAIT_CAUSE_NONE,
+        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
+        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
+    };
+
+    InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
+    nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
+    nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
+    bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
+    sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+
+    // Contains the last window which received a hover event.
+    sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
+
+    // Finding targets for input events.
+    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+                                        const sp<InputApplicationHandle>& applicationHandle,
+                                        const sp<InputWindowHandle>& windowHandle,
+                                        nsecs_t* nextWakeupTime, const char* reason)
+            REQUIRES(mLock);
+
+    void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
+    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+                                                 const sp<InputChannel>& inputChannel)
+            REQUIRES(mLock);
+    nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
+    void resetANRTimeoutsLocked() REQUIRES(mLock);
+
+    int32_t getTargetDisplayId(const EventEntry* entry);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+                                           std::vector<InputTarget>& inputTargets,
+                                           nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+                                           std::vector<InputTarget>& inputTargets,
+                                           nsecs_t* nextWakeupTime,
+                                           bool* outConflictingPointerActions) REQUIRES(mLock);
+    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
+            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+            REQUIRES(mLock);
+    void addGestureMonitors(const std::vector<Monitor>& monitors,
+                            std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+                            float yOffset = 0);
+
+    void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
+                               BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
+            REQUIRES(mLock);
+    void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+                                   std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+    void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
+                                          float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+
+    void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
+    bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
+                                  const InjectionState* injectionState);
+    bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
+                                       int32_t y) const REQUIRES(mLock);
+    bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+    std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
+                                          const sp<InputWindowHandle>& windowHandle);
+
+    std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
+                                                   const sp<InputWindowHandle>& windowHandle,
+                                                   const EventEntry* eventEntry,
+                                                   const char* targetType) REQUIRES(mLock);
+
+    // Manage the dispatch cycle for a single connection.
+    // These methods are deliberately not Interruptible because doing all of the work
+    // with the mutex held makes it easier to ensure that connection invariants are maintained.
+    // If needed, the methods post commands to run later once the critical bits are done.
+    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+                                    EventEntry* eventEntry, const InputTarget* inputTarget)
+            REQUIRES(mLock);
+    void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
+                                      EventEntry* eventEntry, const InputTarget* inputTarget)
+            REQUIRES(mLock);
+    void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
+                                    const InputTarget* inputTarget, int32_t dispatchMode)
+            REQUIRES(mLock);
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+            REQUIRES(mLock);
+    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+                                   uint32_t seq, bool handled) REQUIRES(mLock);
+    void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+                                        bool notify) REQUIRES(mLock);
+    void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
+    void releaseDispatchEntry(DispatchEntry* dispatchEntry);
+    static int handleReceiveCallback(int fd, int events, void* data);
+    // The action sent should only be of type AMOTION_EVENT_*
+    void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+                                         const sp<IBinder>& newToken) REQUIRES(mLock);
+
+    void synthesizeCancelationEventsForAllConnectionsLocked(const CancelationOptions& options)
+            REQUIRES(mLock);
+    void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options)
+            REQUIRES(mLock);
+    void synthesizeCancelationEventsForMonitorsLocked(
+            const CancelationOptions& options,
+            std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
+    void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
+                                                          const CancelationOptions& options)
+            REQUIRES(mLock);
+    void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
+                                                        const CancelationOptions& options)
+            REQUIRES(mLock);
+
+    // Splitting motion events across windows.
+    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
+    // Reset and drop everything the dispatcher is doing.
+    void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
+
+    // Dump state.
+    void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
+    void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
+    void logDispatchStateLocked() REQUIRES(mLock);
+
+    // Registration.
+    void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+    void removeMonitorChannelLocked(
+            const sp<InputChannel>& inputChannel,
+            std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
+    status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+            REQUIRES(mLock);
+
+    // Interesting events that we might like to log or tell the framework about.
+    void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
+                                       uint32_t seq, bool handled) REQUIRES(mLock);
+    void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
+            REQUIRES(mLock);
+    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+                              const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
+    void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
+                     const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
+                     nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+
+    // Outbound policy interactions.
+    void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
+            REQUIRES(mLock);
+    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
+            REQUIRES(mLock);
+    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+                                          DispatchEntry* dispatchEntry, KeyEntry* keyEntry,
+                                          bool handled) REQUIRES(mLock);
+    bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+                                             DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
+                                             bool handled) REQUIRES(mLock);
+    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+
+    // Statistics gathering.
+    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+                                  int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+    void traceInboundQueueLengthLocked() REQUIRES(mLock);
+    void traceOutboundQueueLength(const sp<Connection>& connection);
+    void traceWaitQueueLength(const sp<Connection>& connection);
+
+    sp<InputReporterInterface> mReporter;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
similarity index 64%
copy from services/inputflinger/InputReaderFactory.cpp
copy to services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index 072499b..8d7fa75 100644
--- a/services/inputflinger/InputReaderFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#include "InputReaderFactory.h"
-#include "InputReader.h"
+#include "InputDispatcherFactory.h"
+#include "InputDispatcher.h"
 
 namespace android {
 
-sp<InputReaderInterface> createInputReader(
-        const sp<InputReaderPolicyInterface>& policy,
-        const sp<InputListenerInterface>& listener) {
-    return new InputReader(std::make_unique<EventHub>(), policy, listener);
+sp<InputDispatcherInterface> createInputDispatcher(
+        const sp<InputDispatcherPolicyInterface>& policy) {
+    return new android::inputdispatcher::InputDispatcher(policy);
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
new file mode 100644
index 0000000..18b1b8c
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputDispatcherThread.h"
+
+#include "InputDispatcherInterface.h"
+
+namespace android {
+
+InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher)
+      : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {}
+
+InputDispatcherThread::~InputDispatcherThread() {}
+
+bool InputDispatcherThread::threadLoop() {
+    mDispatcher->dispatchOnce();
+    return true;
+}
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
new file mode 100644
index 0000000..c60700e
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputState.h"
+
+namespace android::inputdispatcher {
+
+InputState::InputState() {}
+
+InputState::~InputState() {}
+
+bool InputState::isNeutral() const {
+    return mKeyMementos.empty() && mMotionMementos.empty();
+}
+
+bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
+    for (const MotionMemento& memento : mMotionMementos) {
+        if (memento.deviceId == deviceId && memento.source == source &&
+            memento.displayId == displayId && memento.hovering) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) {
+    switch (action) {
+        case AKEY_EVENT_ACTION_UP: {
+            if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+                for (size_t i = 0; i < mFallbackKeys.size();) {
+                    if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+                        mFallbackKeys.removeItemsAt(i);
+                    } else {
+                        i += 1;
+                    }
+                }
+            }
+            ssize_t index = findKeyMemento(entry);
+            if (index >= 0) {
+                mKeyMementos.erase(mKeyMementos.begin() + index);
+                return true;
+            }
+            /* FIXME: We can't just drop the key up event because that prevents creating
+             * popup windows that are automatically shown when a key is held and then
+             * dismissed when the key is released.  The problem is that the popup will
+             * not have received the original key down, so the key up will be considered
+             * to be inconsistent with its observed state.  We could perhaps handle this
+             * by synthesizing a key down but that will cause other problems.
+             *
+             * So for now, allow inconsistent key up events to be dispatched.
+             *
+    #if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
+                    "keyCode=%d, scanCode=%d",
+                    entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+    #endif
+            return false;
+            */
+            return true;
+        }
+
+        case AKEY_EVENT_ACTION_DOWN: {
+            ssize_t index = findKeyMemento(entry);
+            if (index >= 0) {
+                mKeyMementos.erase(mKeyMementos.begin() + index);
+            }
+            addKeyMemento(entry, flags);
+            return true;
+        }
+
+        default:
+            return true;
+    }
+}
+
+bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) {
+    int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+    switch (actionMasked) {
+        case AMOTION_EVENT_ACTION_UP:
+        case AMOTION_EVENT_ACTION_CANCEL: {
+            ssize_t index = findMotionMemento(entry, false /*hovering*/);
+            if (index >= 0) {
+                mMotionMementos.erase(mMotionMementos.begin() + index);
+                return true;
+            }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
+                  "displayId=%" PRId32 ", actionMasked=%d",
+                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+#endif
+            return false;
+        }
+
+        case AMOTION_EVENT_ACTION_DOWN: {
+            ssize_t index = findMotionMemento(entry, false /*hovering*/);
+            if (index >= 0) {
+                mMotionMementos.erase(mMotionMementos.begin() + index);
+            }
+            addMotionMemento(entry, flags, false /*hovering*/);
+            return true;
+        }
+
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+        case AMOTION_EVENT_ACTION_POINTER_DOWN:
+        case AMOTION_EVENT_ACTION_MOVE: {
+            if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+                // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
+                // to generate cancellation events for these since they're based in relative rather
+                // than absolute units.
+                return true;
+            }
+
+            ssize_t index = findMotionMemento(entry, false /*hovering*/);
+
+            if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+                // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
+                // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
+                // Any other value and we need to track the motion so we can send cancellation
+                // events for anything generating fallback events (e.g. DPad keys for joystick
+                // movements).
+                if (index >= 0) {
+                    if (entry->pointerCoords[0].isEmpty()) {
+                        mMotionMementos.erase(mMotionMementos.begin() + index);
+                    } else {
+                        MotionMemento& memento = mMotionMementos[index];
+                        memento.setPointers(entry);
+                    }
+                } else if (!entry->pointerCoords[0].isEmpty()) {
+                    addMotionMemento(entry, flags, false /*hovering*/);
+                }
+
+                // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
+                return true;
+            }
+            if (index >= 0) {
+                MotionMemento& memento = mMotionMementos[index];
+                memento.setPointers(entry);
+                return true;
+            }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Dropping inconsistent motion pointer up/down or move event: "
+                  "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
+                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+#endif
+            return false;
+        }
+
+        case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+            ssize_t index = findMotionMemento(entry, true /*hovering*/);
+            if (index >= 0) {
+                mMotionMementos.erase(mMotionMementos.begin() + index);
+                return true;
+            }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
+                  "displayId=%" PRId32,
+                  entry->deviceId, entry->source, entry->displayId);
+#endif
+            return false;
+        }
+
+        case AMOTION_EVENT_ACTION_HOVER_ENTER:
+        case AMOTION_EVENT_ACTION_HOVER_MOVE: {
+            ssize_t index = findMotionMemento(entry, true /*hovering*/);
+            if (index >= 0) {
+                mMotionMementos.erase(mMotionMementos.begin() + index);
+            }
+            addMotionMemento(entry, flags, true /*hovering*/);
+            return true;
+        }
+
+        default:
+            return true;
+    }
+}
+
+ssize_t InputState::findKeyMemento(const KeyEntry* entry) const {
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
+        const KeyMemento& memento = mKeyMementos[i];
+        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
+            memento.displayId == entry->displayId && memento.keyCode == entry->keyCode &&
+            memento.scanCode == entry->scanCode) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos[i];
+        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
+            memento.displayId == entry->displayId && memento.hovering == hovering) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+    KeyMemento memento;
+    memento.deviceId = entry->deviceId;
+    memento.source = entry->source;
+    memento.displayId = entry->displayId;
+    memento.keyCode = entry->keyCode;
+    memento.scanCode = entry->scanCode;
+    memento.metaState = entry->metaState;
+    memento.flags = flags;
+    memento.downTime = entry->downTime;
+    memento.policyFlags = entry->policyFlags;
+    mKeyMementos.push_back(memento);
+}
+
+void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) {
+    MotionMemento memento;
+    memento.deviceId = entry->deviceId;
+    memento.source = entry->source;
+    memento.displayId = entry->displayId;
+    memento.flags = flags;
+    memento.xPrecision = entry->xPrecision;
+    memento.yPrecision = entry->yPrecision;
+    memento.xCursorPosition = entry->xCursorPosition;
+    memento.yCursorPosition = entry->yCursorPosition;
+    memento.downTime = entry->downTime;
+    memento.setPointers(entry);
+    memento.hovering = hovering;
+    memento.policyFlags = entry->policyFlags;
+    mMotionMementos.push_back(memento);
+}
+
+void InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+    pointerCount = entry->pointerCount;
+    for (uint32_t i = 0; i < entry->pointerCount; i++) {
+        pointerProperties[i].copyFrom(entry->pointerProperties[i]);
+        pointerCoords[i].copyFrom(entry->pointerCoords[i]);
+    }
+}
+
+void InputState::synthesizeCancelationEvents(nsecs_t currentTime,
+                                             std::vector<EventEntry*>& outEvents,
+                                             const CancelationOptions& options) {
+    for (KeyMemento& memento : mKeyMementos) {
+        if (shouldCancelKey(memento, options)) {
+            outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+                                             memento.deviceId, memento.source, memento.displayId,
+                                             memento.policyFlags, AKEY_EVENT_ACTION_UP,
+                                             memento.flags | AKEY_EVENT_FLAG_CANCELED,
+                                             memento.keyCode, memento.scanCode, memento.metaState,
+                                             0, memento.downTime));
+        }
+    }
+
+    for (const MotionMemento& memento : mMotionMementos) {
+        if (shouldCancelMotion(memento, options)) {
+            const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
+                                                    : AMOTION_EVENT_ACTION_CANCEL;
+            outEvents.push_back(
+                    new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
+                                    memento.source, memento.displayId, memento.policyFlags, action,
+                                    0 /*actionButton*/, memento.flags, AMETA_NONE,
+                                    0 /*buttonState*/, MotionClassification::NONE,
+                                    AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+                                    memento.yPrecision, memento.xCursorPosition,
+                                    memento.yCursorPosition, memento.downTime, memento.pointerCount,
+                                    memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
+                                    0 /*yOffset*/));
+        }
+    }
+}
+
+void InputState::clear() {
+    mKeyMementos.clear();
+    mMotionMementos.clear();
+    mFallbackKeys.clear();
+}
+
+void InputState::copyPointerStateTo(InputState& other) const {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos[i];
+        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+            for (size_t j = 0; j < other.mMotionMementos.size();) {
+                const MotionMemento& otherMemento = other.mMotionMementos[j];
+                if (memento.deviceId == otherMemento.deviceId &&
+                    memento.source == otherMemento.source &&
+                    memento.displayId == otherMemento.displayId) {
+                    other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
+                } else {
+                    j += 1;
+                }
+            }
+            other.mMotionMementos.push_back(memento);
+        }
+    }
+}
+
+int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
+    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+    return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
+}
+
+void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) {
+    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+    if (index >= 0) {
+        mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
+    } else {
+        mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
+    }
+}
+
+void InputState::removeFallbackKey(int32_t originalKeyCode) {
+    mFallbackKeys.removeItem(originalKeyCode);
+}
+
+bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) {
+    if (options.keyCode && memento.keyCode != options.keyCode.value()) {
+        return false;
+    }
+
+    if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+        return false;
+    }
+
+    if (options.displayId && memento.displayId != options.displayId.value()) {
+        return false;
+    }
+
+    switch (options.mode) {
+        case CancelationOptions::CANCEL_ALL_EVENTS:
+        case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+            return true;
+        case CancelationOptions::CANCEL_FALLBACK_EVENTS:
+            return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
+        default:
+            return false;
+    }
+}
+
+bool InputState::shouldCancelMotion(const MotionMemento& memento,
+                                    const CancelationOptions& options) {
+    if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+        return false;
+    }
+
+    if (options.displayId && memento.displayId != options.displayId.value()) {
+        return false;
+    }
+
+    switch (options.mode) {
+        case CancelationOptions::CANCEL_ALL_EVENTS:
+            return true;
+        case CancelationOptions::CANCEL_POINTER_EVENTS:
+            return memento.source & AINPUT_SOURCE_CLASS_POINTER;
+        case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+            return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+        default:
+            return false;
+    }
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
new file mode 100644
index 0000000..bccef0f
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+
+#include "CancelationOptions.h"
+#include "Entry.h"
+
+#include <utils/Timers.h>
+
+namespace android::inputdispatcher {
+
+// Sequence number for synthesized or injected events.
+constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+
+/* Tracks dispatched key and motion event state so that cancellation events can be
+ * synthesized when events are dropped. */
+class InputState {
+public:
+    InputState();
+    ~InputState();
+
+    // Returns true if there is no state to be canceled.
+    bool isNeutral() const;
+
+    // Returns true if the specified source is known to have received a hover enter
+    // motion event.
+    bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
+
+    // Records tracking information for a key event that has just been published.
+    // Returns true if the event should be delivered, false if it is inconsistent
+    // and should be skipped.
+    bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
+
+    // Records tracking information for a motion event that has just been published.
+    // Returns true if the event should be delivered, false if it is inconsistent
+    // and should be skipped.
+    bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
+
+    // Synthesizes cancelation events for the current state and resets the tracked state.
+    void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents,
+                                     const CancelationOptions& options);
+
+    // Clears the current state.
+    void clear();
+
+    // Copies pointer-related parts of the input state to another instance.
+    void copyPointerStateTo(InputState& other) const;
+
+    // Gets the fallback key associated with a keycode.
+    // Returns -1 if none.
+    // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
+    int32_t getFallbackKey(int32_t originalKeyCode);
+
+    // Sets the fallback key for a particular keycode.
+    void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
+
+    // Removes the fallback key for a particular keycode.
+    void removeFallbackKey(int32_t originalKeyCode);
+
+    inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const { return mFallbackKeys; }
+
+private:
+    struct KeyMemento {
+        int32_t deviceId;
+        uint32_t source;
+        int32_t displayId;
+        int32_t keyCode;
+        int32_t scanCode;
+        int32_t metaState;
+        int32_t flags;
+        nsecs_t downTime;
+        uint32_t policyFlags;
+    };
+
+    struct MotionMemento {
+        int32_t deviceId;
+        uint32_t source;
+        int32_t displayId;
+        int32_t flags;
+        float xPrecision;
+        float yPrecision;
+        float xCursorPosition;
+        float yCursorPosition;
+        nsecs_t downTime;
+        uint32_t pointerCount;
+        PointerProperties pointerProperties[MAX_POINTERS];
+        PointerCoords pointerCoords[MAX_POINTERS];
+        bool hovering;
+        uint32_t policyFlags;
+
+        void setPointers(const MotionEntry* entry);
+    };
+
+    std::vector<KeyMemento> mKeyMementos;
+    std::vector<MotionMemento> mMotionMementos;
+    KeyedVector<int32_t, int32_t> mFallbackKeys;
+
+    ssize_t findKeyMemento(const KeyEntry* entry) const;
+    ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+
+    void addKeyMemento(const KeyEntry* entry, int32_t flags);
+    void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+
+    static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
+    static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
new file mode 100644
index 0000000..80fa2cb
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputTarget.h"
+
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
+#include <string>
+
+using android::base::StringPrintf;
+
+namespace android::inputdispatcher {
+
+std::string dispatchModeToString(int32_t dispatchMode) {
+    switch (dispatchMode) {
+        case InputTarget::FLAG_DISPATCH_AS_IS:
+            return "DISPATCH_AS_IS";
+        case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
+            return "DISPATCH_AS_OUTSIDE";
+        case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
+            return "DISPATCH_AS_HOVER_ENTER";
+        case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
+            return "DISPATCH_AS_HOVER_EXIT";
+        case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
+            return "DISPATCH_AS_SLIPPERY_EXIT";
+        case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
+            return "DISPATCH_AS_SLIPPERY_ENTER";
+    }
+    return StringPrintf("%" PRId32, dispatchMode);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
new file mode 100644
index 0000000..5acf92b
--- /dev/null
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+
+#include <input/InputTransport.h>
+#include <utils/BitSet.h>
+#include <utils/RefBase.h>
+
+namespace android::inputdispatcher {
+
+/*
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ */
+struct InputTarget {
+    enum {
+        /* This flag indicates that the event is being delivered to a foreground application. */
+        FLAG_FOREGROUND = 1 << 0,
+
+        /* This flag indicates that the MotionEvent falls within the area of the target
+         * obscured by another visible window above it.  The motion event should be
+         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
+        FLAG_WINDOW_IS_OBSCURED = 1 << 1,
+
+        /* This flag indicates that a motion event is being split across multiple windows. */
+        FLAG_SPLIT = 1 << 2,
+
+        /* This flag indicates that the pointer coordinates dispatched to the application
+         * will be zeroed out to avoid revealing information to an application. This is
+         * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
+         * the same UID from watching all touches. */
+        FLAG_ZERO_COORDS = 1 << 3,
+
+        /* This flag indicates that the event should be sent as is.
+         * Should always be set unless the event is to be transmuted. */
+        FLAG_DISPATCH_AS_IS = 1 << 8,
+
+        /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
+         * of the area of this target and so should instead be delivered as an
+         * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
+        FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
+
+        /* This flag indicates that a hover sequence is starting in the given window.
+         * The event is transmuted into ACTION_HOVER_ENTER. */
+        FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
+
+        /* This flag indicates that a hover event happened outside of a window which handled
+         * previous hover events, signifying the end of the current hover sequence for that
+         * window.
+         * The event is transmuted into ACTION_HOVER_ENTER. */
+        FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
+
+        /* This flag indicates that the event should be canceled.
+         * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
+         * outside of a window. */
+        FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
+
+        /* This flag indicates that the event should be dispatched as an initial down.
+         * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
+         * into a new window. */
+        FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
+
+        /* Mask for all dispatch modes. */
+        FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE |
+                FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT |
+                FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
+
+        /* This flag indicates that the target of a MotionEvent is partly or wholly
+         * obscured by another visible window above it.  The motion event should be
+         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
+        FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
+
+    };
+
+    // The input channel to be targeted.
+    sp<InputChannel> inputChannel;
+
+    // Flags for the input target.
+    int32_t flags;
+
+    // The x and y offset to add to a MotionEvent as it is delivered.
+    // (ignored for KeyEvents)
+    float xOffset, yOffset;
+
+    // Scaling factor to apply to MotionEvent as it is delivered.
+    // (ignored for KeyEvents)
+    float globalScaleFactor;
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+
+    // The subset of pointer ids to include in motion events dispatched to this input target
+    // if FLAG_SPLIT is set.
+    BitSet32 pointerIds;
+};
+
+std::string dispatchModeToString(int32_t dispatchMode);
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
new file mode 100644
index 0000000..289b084
--- /dev/null
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Monitor.h"
+
+namespace android::inputdispatcher {
+
+// --- Monitor ---
+Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+
+// --- TouchedMonitor ---
+TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
+      : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
new file mode 100644
index 0000000..b67c9eb
--- /dev/null
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+
+#include <input/InputTransport.h>
+
+namespace android::inputdispatcher {
+
+struct Monitor {
+    sp<InputChannel> inputChannel; // never null
+
+    explicit Monitor(const sp<InputChannel>& inputChannel);
+};
+
+// For tracking the offsets we need to apply when adding gesture monitor targets.
+struct TouchedMonitor {
+    Monitor monitor;
+    float xOffset = 0.f;
+    float yOffset = 0.f;
+
+    explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
new file mode 100644
index 0000000..18848a0
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputWindow.h>
+
+#include "InputTarget.h"
+
+#include "TouchState.h"
+
+using android::InputWindowHandle;
+
+namespace android::inputdispatcher {
+
+TouchState::TouchState()
+      : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {}
+
+TouchState::~TouchState() {}
+
+void TouchState::reset() {
+    down = false;
+    split = false;
+    deviceId = -1;
+    source = 0;
+    displayId = ADISPLAY_ID_NONE;
+    windows.clear();
+    portalWindows.clear();
+    gestureMonitors.clear();
+}
+
+void TouchState::copyFrom(const TouchState& other) {
+    down = other.down;
+    split = other.split;
+    deviceId = other.deviceId;
+    source = other.source;
+    displayId = other.displayId;
+    windows = other.windows;
+    portalWindows = other.portalWindows;
+    gestureMonitors = other.gestureMonitors;
+}
+
+void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
+                                   BitSet32 pointerIds) {
+    if (targetFlags & InputTarget::FLAG_SPLIT) {
+        split = true;
+    }
+
+    for (size_t i = 0; i < windows.size(); i++) {
+        TouchedWindow& touchedWindow = windows[i];
+        if (touchedWindow.windowHandle == windowHandle) {
+            touchedWindow.targetFlags |= targetFlags;
+            if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+                touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
+            }
+            touchedWindow.pointerIds.value |= pointerIds.value;
+            return;
+        }
+    }
+
+    TouchedWindow touchedWindow;
+    touchedWindow.windowHandle = windowHandle;
+    touchedWindow.targetFlags = targetFlags;
+    touchedWindow.pointerIds = pointerIds;
+    windows.push_back(touchedWindow);
+}
+
+void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+    size_t numWindows = portalWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        if (portalWindows[i] == windowHandle) {
+            return;
+        }
+    }
+    portalWindows.push_back(windowHandle);
+}
+
+void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
+    const size_t newSize = gestureMonitors.size() + newMonitors.size();
+    gestureMonitors.reserve(newSize);
+    gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors),
+                           std::end(newMonitors));
+}
+
+void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows[i].windowHandle == windowHandle) {
+            windows.erase(windows.begin() + i);
+            return;
+        }
+    }
+}
+
+void TouchState::removeWindowByToken(const sp<IBinder>& token) {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows[i].windowHandle->getToken() == token) {
+            windows.erase(windows.begin() + i);
+            return;
+        }
+    }
+}
+
+void TouchState::filterNonAsIsTouchWindows() {
+    for (size_t i = 0; i < windows.size();) {
+        TouchedWindow& window = windows[i];
+        if (window.targetFlags &
+            (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
+            window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
+            window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
+            i += 1;
+        } else {
+            windows.erase(windows.begin() + i);
+        }
+    }
+}
+
+void TouchState::filterNonMonitors() {
+    windows.clear();
+    portalWindows.clear();
+}
+
+sp<InputWindowHandle> TouchState::getFirstForegroundWindowHandle() const {
+    for (size_t i = 0; i < windows.size(); i++) {
+        const TouchedWindow& window = windows[i];
+        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            return window.windowHandle;
+        }
+    }
+    return nullptr;
+}
+
+bool TouchState::isSlippery() const {
+    // Must have exactly one foreground window.
+    bool haveSlipperyForegroundWindow = false;
+    for (const TouchedWindow& window : windows) {
+        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            if (haveSlipperyForegroundWindow ||
+                !(window.windowHandle->getInfo()->layoutParamsFlags &
+                  InputWindowInfo::FLAG_SLIPPERY)) {
+                return false;
+            }
+            haveSlipperyForegroundWindow = true;
+        }
+    }
+    return haveSlipperyForegroundWindow;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
new file mode 100644
index 0000000..3e0e0eb
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+
+#include "Monitor.h"
+#include "TouchedWindow.h"
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+
+struct TouchState {
+    bool down;
+    bool split;
+    int32_t deviceId;  // id of the device that is currently down, others are rejected
+    uint32_t source;   // source of the device that is current down, others are rejected
+    int32_t displayId; // id to the display that currently has a touch, others are rejected
+    std::vector<TouchedWindow> windows;
+
+    // This collects the portal windows that the touch has gone through. Each portal window
+    // targets a display (embedded display for most cases). With this info, we can add the
+    // monitoring channels of the displays touched.
+    std::vector<sp<android::InputWindowHandle>> portalWindows;
+
+    std::vector<TouchedMonitor> gestureMonitors;
+
+    TouchState();
+    ~TouchState();
+    void reset();
+    void copyFrom(const TouchState& other);
+    void addOrUpdateWindow(const sp<android::InputWindowHandle>& windowHandle, int32_t targetFlags,
+                           BitSet32 pointerIds);
+    void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle);
+    void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
+    void removeWindow(const sp<android::InputWindowHandle>& windowHandle);
+    void removeWindowByToken(const sp<IBinder>& token);
+    void filterNonAsIsTouchWindows();
+    void filterNonMonitors();
+    sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
+    bool isSlippery() const;
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
new file mode 100644
index 0000000..8713aa3
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+
+namespace android {
+
+class InputWindowHandle;
+
+namespace inputdispatcher {
+
+// Focus tracking for touch.
+struct TouchedWindow {
+    sp<android::InputWindowHandle> windowHandle;
+    int32_t targetFlags;
+    BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
+};
+
+} // namespace inputdispatcher
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
new file mode 100644
index 0000000..00abf47
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/*
+ * Input dispatcher configuration.
+ *
+ * Specifies various options that modify the behavior of the input dispatcher.
+ * The values provided here are merely defaults. The actual values will come from ViewConfiguration
+ * and are passed into the dispatcher during initialization.
+ */
+struct InputDispatcherConfiguration {
+    // The key repeat initial timeout.
+    nsecs_t keyRepeatTimeout;
+
+    // The key repeat inter-key delay.
+    nsecs_t keyRepeatDelay;
+
+    InputDispatcherConfiguration()
+          : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {}
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
new file mode 100644
index 0000000..a359557
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+
+#include <utils/RefBase.h>
+
+#include "InputDispatcherInterface.h"
+#include "InputDispatcherPolicyInterface.h"
+
+namespace android {
+
+// This factory method is used to encapsulate implementation details in internal header files.
+sp<InputDispatcherInterface> createInputDispatcher(
+        const sp<InputDispatcherPolicyInterface>& policy);
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
new file mode 100644
index 0000000..9329ca6
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+
+#include <InputListener.h>
+#include <input/ISetInputWindowsListener.h>
+
+namespace android {
+
+class InputApplicationHandle;
+class InputChannel;
+class InputWindowHandle;
+
+/*
+ * Constants used to report the outcome of input event injection.
+ */
+enum {
+    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+    INPUT_EVENT_INJECTION_PENDING = -1,
+
+    /* Injection succeeded. */
+    INPUT_EVENT_INJECTION_SUCCEEDED = 0,
+
+    /* Injection failed because the injector did not have permission to inject
+     * into the application with input focus. */
+    INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
+
+    /* Injection failed because there were no available input targets. */
+    INPUT_EVENT_INJECTION_FAILED = 2,
+
+    /* Injection failed due to a timeout. */
+    INPUT_EVENT_INJECTION_TIMED_OUT = 3
+};
+
+/* Notifies the system about input events generated by the input reader.
+ * The dispatcher is expected to be mostly asynchronous. */
+class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
+protected:
+    InputDispatcherInterface() {}
+    virtual ~InputDispatcherInterface() {}
+
+public:
+    /* Dumps the state of the input dispatcher.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(std::string& dump) = 0;
+
+    /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
+    virtual void monitor() = 0;
+
+    /* Runs a single iteration of the dispatch loop.
+     * Nominally processes one queued event, a timeout, or a response from an input consumer.
+     *
+     * This method should only be called on the input dispatcher thread.
+     */
+    virtual void dispatchOnce() = 0;
+
+    /* Injects an input event and optionally waits for sync.
+     * The synchronization mode determines whether the method blocks while waiting for
+     * input injection to proceed.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
+                                     int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+                                     uint32_t policyFlags) = 0;
+
+    /* Sets the list of input windows.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setInputWindows(
+            const std::vector<sp<InputWindowHandle> >& inputWindowHandles, int32_t displayId,
+            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
+
+    /* Sets the focused application on the given display.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setFocusedApplication(
+            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+    /* Sets the focused display.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setFocusedDisplay(int32_t displayId) = 0;
+
+    /* Sets the input dispatching mode.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+
+    /* Sets whether input event filtering is enabled.
+     * When enabled, incoming input events are sent to the policy's filterInputEvent
+     * method instead of being dispatched.  The filter is expected to use
+     * injectInputEvent to inject the events it would like to have dispatched.
+     * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
+     */
+    virtual void setInputFilterEnabled(bool enabled) = 0;
+
+    /* Transfers touch focus from one window to another window.
+     *
+     * Returns true on success.  False if the window did not actually have touch focus.
+     */
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+
+    /* Registers input channels that may be used as targets for input events.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
+                                          int32_t displayId) = 0;
+
+    /* Registers input channels to be used to monitor input events.
+     *
+     * Each monitor must target a specific display and will only receive input events sent to that
+     * display. If the monitor is a gesture monitor, it will only receive pointer events on the
+     * targeted display.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
+                                          bool gestureMonitor) = 0;
+
+    /* Unregister input channels that will no longer receive input events.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+
+    /* Allows an input monitor steal the current pointer stream away from normal input windows.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
new file mode 100644
index 0000000..4214488
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+
+#include "InputDispatcherConfiguration.h"
+
+#include <binder/IBinder.h>
+#include <input/Input.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class InputApplicationHandle;
+
+/*
+ * Input dispatcher policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class InputDispatcherPolicyInterface : public virtual RefBase {
+protected:
+    InputDispatcherPolicyInterface() {}
+    virtual ~InputDispatcherPolicyInterface() {}
+
+public:
+    /* Notifies the system that a configuration change has occurred. */
+    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
+    /* Notifies the system that an application is not responding.
+     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+                              const sp<IBinder>& token, const std::string& reason) = 0;
+
+    /* Notifies the system that an input channel is unrecoverably broken. */
+    virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+    virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
+
+    /* Gets the input dispatcher configuration. */
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
+
+    /* Filters an input event.
+     * Return true to dispatch the event unmodified, false to consume the event.
+     * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
+     * to injectInputEvent.
+     */
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+
+    /* Intercepts a key event immediately before queueing it.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing such as updating policy flags.
+     *
+     * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
+     * should be dispatched to applications.
+     */
+    virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
+
+    /* Intercepts a touch, trackball or other motion event before queueing it.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing such as updating policy flags.
+     *
+     * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
+     * should be dispatched to applications.
+     */
+    virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+                                               uint32_t& policyFlags) = 0;
+
+    /* Allows the policy a chance to intercept a key before dispatching. */
+    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
+                                                  const KeyEvent* keyEvent,
+                                                  uint32_t policyFlags) = 0;
+
+    /* Allows the policy a chance to perform default processing for an unhandled key.
+     * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
+    virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+                                      uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
+
+    /* Notifies the policy about switch events.
+     */
+    virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+                              uint32_t policyFlags) = 0;
+
+    /* Poke user activity for an event dispatched to a window. */
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+
+    /* Checks whether a given application pid/uid has permission to inject input events
+     * into other applications.
+     *
+     * This method is special in that its implementation promises to be non-reentrant and
+     * is safe to call while holding other locks.  (Most other methods make no such guarantees!)
+     */
+    virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
+                                                         int32_t injectorUid) = 0;
+
+    /* Notifies the policy that a pointer down event has occurred outside the current focused
+     * window.
+     *
+     * The touchedToken passed as an argument is the window that received the input event.
+     */
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
new file mode 100644
index 0000000..2604959
--- /dev/null
+++ b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
+#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
+
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class InputDispatcherInterface;
+
+/* Enqueues and dispatches input events, endlessly. */
+class InputDispatcherThread : public Thread {
+public:
+    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
+    ~InputDispatcherThread();
+
+private:
+    virtual bool threadLoop();
+
+    sp<InputDispatcherInterface> mDispatcher;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
new file mode 100644
index 0000000..a64f4dd
--- /dev/null
+++ b/services/inputflinger/reader/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+    name: "libinputreader_headers",
+    export_include_dirs: [
+        "include",
+        "mapper",
+        "mapper/accumulator",
+    ],
+}
+
+cc_library_shared {
+    name: "libinputreader",
+    defaults: ["inputflinger_defaults"],
+
+    srcs: [
+        "EventHub.cpp",
+        "InputDevice.cpp",
+        "mapper/accumulator/CursorButtonAccumulator.cpp",
+        "mapper/accumulator/CursorScrollAccumulator.cpp",
+        "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
+        "mapper/accumulator/TouchButtonAccumulator.cpp",
+        "mapper/CursorInputMapper.cpp",
+        "mapper/ExternalStylusInputMapper.cpp",
+        "mapper/InputMapper.cpp",
+        "mapper/JoystickInputMapper.cpp",
+        "mapper/KeyboardInputMapper.cpp",
+        "mapper/MultiTouchInputMapper.cpp",
+        "mapper/RotaryEncoderInputMapper.cpp",
+        "mapper/SingleTouchInputMapper.cpp",
+        "mapper/SwitchInputMapper.cpp",
+        "mapper/TouchInputMapper.cpp",
+        "mapper/VibratorInputMapper.cpp",
+        "InputReader.cpp",
+        "InputReaderFactory.cpp",
+        "TouchVideoDevice.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libinputflinger_base",
+        "libcrypto",
+        "libcutils",
+        "libinput",
+        "liblog",
+        "libui",
+        "libutils",
+        "libhardware_legacy",
+    ],
+
+    header_libs: [
+        "libinputflinger_headers",
+        "libinputreader_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+}
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
similarity index 83%
rename from services/inputflinger/EventHub.cpp
rename to services/inputflinger/reader/EventHub.cpp
index af02314..c15ecfd 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -25,9 +25,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
-#include <sys/limits.h>
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
+#include <sys/limits.h>
 #include <unistd.h>
 
 #define LOG_TAG "EventHub"
@@ -41,13 +41,13 @@
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <openssl/sha.h>
+#include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
-#include <utils/Errors.h>
 
-#include <input/KeyLayoutMap.h>
 #include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
 #include <input/VirtualKeyMap.h>
 
 /* this macro is used to tell if "bit" is set in "array"
@@ -55,10 +55,10 @@
  * operation with a byte that only has the relevant bit set.
  * eg. to check for the 12th bit, we do (array[1] & 1<<4)
  */
-#define test_bit(bit, array)    ((array)[(bit)/8] & (1<<((bit)%8)))
+#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
 
 /* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits)  (((bits) + 7) / 8)
+#define sizeof_bit_array(bits) (((bits) + 7) / 8)
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -70,10 +70,10 @@
 
 static constexpr bool DEBUG = false;
 
-static const char *WAKE_LOCK_ID = "KeyEvents";
-static const char *DEVICE_PATH = "/dev/input";
+static const char* WAKE_LOCK_ID = "KeyEvents";
+static const char* DEVICE_PATH = "/dev/input";
 // v4l2 devices go directly into /dev
-static const char *VIDEO_DEVICE_PATH = "/dev";
+static const char* VIDEO_DEVICE_PATH = "/dev";
 
 static inline const char* toString(bool value) {
     return value ? "true" : "false";
@@ -117,7 +117,7 @@
  * directly from /dev.
  */
 static bool isV4lScanningEnabled() {
-  return property_get_bool("ro.input.video_enabled", true /* default_value */);
+    return property_get_bool("ro.input.video_enabled", true /* default_value */);
 }
 
 static nsecs_t processEventTimestamp(const struct input_event& event) {
@@ -144,27 +144,27 @@
     // Touch devices get dibs on touch-related axes.
     if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
         switch (axis) {
-        case ABS_X:
-        case ABS_Y:
-        case ABS_PRESSURE:
-        case ABS_TOOL_WIDTH:
-        case ABS_DISTANCE:
-        case ABS_TILT_X:
-        case ABS_TILT_Y:
-        case ABS_MT_SLOT:
-        case ABS_MT_TOUCH_MAJOR:
-        case ABS_MT_TOUCH_MINOR:
-        case ABS_MT_WIDTH_MAJOR:
-        case ABS_MT_WIDTH_MINOR:
-        case ABS_MT_ORIENTATION:
-        case ABS_MT_POSITION_X:
-        case ABS_MT_POSITION_Y:
-        case ABS_MT_TOOL_TYPE:
-        case ABS_MT_BLOB_ID:
-        case ABS_MT_TRACKING_ID:
-        case ABS_MT_PRESSURE:
-        case ABS_MT_DISTANCE:
-            return INPUT_DEVICE_CLASS_TOUCH;
+            case ABS_X:
+            case ABS_Y:
+            case ABS_PRESSURE:
+            case ABS_TOOL_WIDTH:
+            case ABS_DISTANCE:
+            case ABS_TILT_X:
+            case ABS_TILT_Y:
+            case ABS_MT_SLOT:
+            case ABS_MT_TOUCH_MAJOR:
+            case ABS_MT_TOUCH_MINOR:
+            case ABS_MT_WIDTH_MAJOR:
+            case ABS_MT_WIDTH_MINOR:
+            case ABS_MT_ORIENTATION:
+            case ABS_MT_POSITION_X:
+            case ABS_MT_POSITION_Y:
+            case ABS_MT_TOOL_TYPE:
+            case ABS_MT_BLOB_ID:
+            case ABS_MT_TRACKING_ID:
+            case ABS_MT_PRESSURE:
+            case ABS_MT_DISTANCE:
+                return INPUT_DEVICE_CLASS_TOUCH;
         }
     }
 
@@ -182,12 +182,20 @@
 // --- EventHub::Device ---
 
 EventHub::Device::Device(int fd, int32_t id, const std::string& path,
-        const InputDeviceIdentifier& identifier) :
-        next(nullptr),
-        fd(fd), id(id), path(path), identifier(identifier),
-        classes(0), configuration(nullptr), virtualKeyMap(nullptr),
-        ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
-        enabled(true), isVirtual(fd < 0) {
+                         const InputDeviceIdentifier& identifier)
+      : next(nullptr),
+        fd(fd),
+        id(id),
+        path(path),
+        identifier(identifier),
+        classes(0),
+        configuration(nullptr),
+        virtualKeyMap(nullptr),
+        ffEffectPlaying(false),
+        ffEffectId(-1),
+        controllerNumber(0),
+        enabled(true),
+        isVirtual(fd < 0) {
     memset(keyBitmask, 0, sizeof(keyBitmask));
     memset(absBitmask, 0, sizeof(absBitmask));
     memset(relBitmask, 0, sizeof(relBitmask));
@@ -211,7 +219,7 @@
 
 status_t EventHub::Device::enable() {
     fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
-    if(fd < 0) {
+    if (fd < 0) {
         ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno));
         return -errno;
     }
@@ -233,12 +241,18 @@
 
 const int EventHub::EPOLL_MAX_EVENTS;
 
-EventHub::EventHub(void) :
-        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
-        mOpeningDevices(nullptr), mClosingDevices(nullptr),
+EventHub::EventHub(void)
+      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
+        mNextDeviceId(1),
+        mControllerNumbers(),
+        mOpeningDevices(nullptr),
+        mClosingDevices(nullptr),
         mNeedToSendFinishedDeviceScan(false),
-        mNeedToReopenDevices(false), mNeedToScanDevices(true),
-        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
+        mNeedToReopenDevices(false),
+        mNeedToScanDevices(true),
+        mPendingEventCount(0),
+        mPendingEventIndex(0),
+        mPendingINotify(false) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
@@ -246,12 +260,12 @@
 
     mINotifyFd = inotify_init();
     mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
-    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
-            DEVICE_PATH, strerror(errno));
+    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
+                        strerror(errno));
     if (isV4lScanningEnabled()) {
         mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
         LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
-                VIDEO_DEVICE_PATH, strerror(errno));
+                            VIDEO_DEVICE_PATH, strerror(errno));
     } else {
         mVideoWd = -1;
         ALOGI("Video device scanning disabled");
@@ -273,16 +287,16 @@
 
     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
-            errno);
+                        errno);
 
     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
-            errno);
+                        errno);
 
     eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
-            errno);
+                        errno);
 }
 
 EventHub::~EventHub(void) {
@@ -334,7 +348,7 @@
 }
 
 status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
-        RawAbsoluteAxisInfo* outAxisInfo) const {
+                                       RawAbsoluteAxisInfo* outAxisInfo) const {
     outAxisInfo->clear();
 
     if (axis >= 0 && axis <= ABS_MAX) {
@@ -343,9 +357,9 @@
         Device* device = getDeviceLocked(deviceId);
         if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
             struct input_absinfo info;
-            if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
-                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
-                     axis, device->identifier.name.c_str(), device->fd, errno);
+            if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
+                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
+                      device->identifier.name.c_str(), device->fd, errno);
                 return -errno;
             }
 
@@ -452,9 +466,9 @@
         Device* device = getDeviceLocked(deviceId);
         if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
             struct input_absinfo info;
-            if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
-                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
-                     axis, device->identifier.name.c_str(), device->fd, errno);
+            if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
+                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
+                      device->identifier.name.c_str(), device->fd, errno);
                 return -errno;
             }
 
@@ -465,8 +479,8 @@
     return -1;
 }
 
-bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) const {
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+                                     uint8_t* outFlags) const {
     AutoMutex _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
@@ -475,9 +489,9 @@
         for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
             scanCodes.clear();
 
-            status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(
-                    keyCodes[codeIndex], &scanCodes);
-            if (! err) {
+            status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex],
+                                                                            &scanCodes);
+            if (!err) {
                 // check the possible scan codes identified by the layout map against the
                 // map of codes actually emitted by the driver
                 for (size_t sc = 0; sc < scanCodes.size(); sc++) {
@@ -493,9 +507,8 @@
     return false;
 }
 
-status_t EventHub::mapKey(int32_t deviceId,
-        int32_t scanCode, int32_t usageCode, int32_t metaState,
-        int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
+status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                          int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     status_t status = NAME_NOT_FOUND;
@@ -602,7 +615,7 @@
 }
 
 void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
-        std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+                                        std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
     outVirtualKeys.clear();
 
     AutoMutex _l(mLock);
@@ -623,15 +636,13 @@
     return nullptr;
 }
 
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
-        const sp<KeyCharacterMap>& map) {
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device) {
         if (map != device->overlayKeyMap) {
             device->overlayKeyMap = map;
-            device->combinedKeyMap = KeyCharacterMap::combine(
-                    device->keyMap.keyCharacterMap, map);
+            device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
             return true;
         }
     }
@@ -640,8 +651,7 @@
 
 static std::string generateDescriptor(InputDeviceIdentifier& identifier) {
     std::string rawDescriptor;
-    rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor,
-            identifier.product);
+    rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, identifier.product);
     // TODO add handling for USB devices to not uniqueify kbs that show up twice
     if (!identifier.uniqueId.empty()) {
         rawDescriptor += "uniqueId:";
@@ -680,13 +690,13 @@
     if (identifier.uniqueId.empty()) {
         // If it didn't have a unique id check for conflicts and enforce
         // uniqueness if necessary.
-        while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
+        while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
             identifier.nonce++;
             rawDescriptor = generateDescriptor(identifier);
         }
     }
     ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(),
-            identifier.descriptor.c_str());
+          identifier.descriptor.c_str());
 }
 
 void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
@@ -703,7 +713,7 @@
         effect.replay.delay = 0;
         if (ioctl(device->fd, EVIOCSFF, &effect)) {
             ALOGW("Could not upload force feedback effect to device %s due to error %d.",
-                    device->identifier.name.c_str(), errno);
+                  device->identifier.name.c_str(), errno);
             return;
         }
         device->ffEffectId = effect.id;
@@ -716,7 +726,7 @@
         ev.value = 1;
         if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
             ALOGW("Could not start force feedback effect on device %s due to error %d.",
-                    device->identifier.name.c_str(), errno);
+                  device->identifier.name.c_str(), errno);
             return;
         }
         device->ffEffectPlaying = true;
@@ -738,7 +748,7 @@
             ev.value = 0;
             if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
                 ALOGW("Could not stop force feedback effect on device %s due to error %d.",
-                        device->identifier.name.c_str(), errno);
+                      device->identifier.name.c_str(), errno);
                 return;
             }
         }
@@ -825,12 +835,12 @@
         // Report any devices that had last been added/removed.
         while (mClosingDevices) {
             Device* device = mClosingDevices;
-            ALOGV("Reporting device closed: id=%d, name=%s\n",
-                 device->id, device->path.c_str());
+            ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
             mClosingDevices = device->next;
             event->when = now;
-            event->deviceId = (device->id == mBuiltInKeyboardId) ?
-                    ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
+            event->deviceId = (device->id == mBuiltInKeyboardId)
+                    ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
+                    : device->id;
             event->type = DEVICE_REMOVED;
             event += 1;
             delete device;
@@ -848,8 +858,7 @@
 
         while (mOpeningDevices != nullptr) {
             Device* device = mOpeningDevices;
-            ALOGV("Reporting device opened: id=%d, name=%s\n",
-                 device->id, device->path.c_str());
+            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
             mOpeningDevices = device->next;
             event->when = now;
             event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
@@ -895,15 +904,15 @@
                     } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                 } else {
                     ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
-                            eventItem.events);
+                          eventItem.events);
                 }
                 continue;
             }
 
             Device* device = getDeviceByFdLocked(eventItem.data.fd);
             if (!device) {
-                ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.",
-                        eventItem.events, eventItem.data.fd);
+                ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
+                      eventItem.data.fd);
                 ALOG_ASSERT(!DEBUG);
                 continue;
             }
@@ -912,30 +921,30 @@
                     size_t numFrames = device->videoDevice->readAndQueueFrames();
                     if (numFrames == 0) {
                         ALOGE("Received epoll event for video device %s, but could not read frame",
-                                device->videoDevice->getName().c_str());
+                              device->videoDevice->getName().c_str());
                     }
                 } else if (eventItem.events & EPOLLHUP) {
                     // TODO(b/121395353) - consider adding EPOLLRDHUP
                     ALOGI("Removing video device %s due to epoll hang-up event.",
-                            device->videoDevice->getName().c_str());
+                          device->videoDevice->getName().c_str());
                     unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
                     device->videoDevice = nullptr;
                 } else {
-                    ALOGW("Received unexpected epoll event 0x%08x for device %s.",
-                            eventItem.events, device->videoDevice->getName().c_str());
+                    ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
+                          device->videoDevice->getName().c_str());
                     ALOG_ASSERT(!DEBUG);
                 }
                 continue;
             }
             // This must be an input event
             if (eventItem.events & EPOLLIN) {
-                int32_t readSize = read(device->fd, readBuffer,
-                        sizeof(struct input_event) * capacity);
+                int32_t readSize =
+                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                     // Device was removed before INotify noticed.
                     ALOGW("could not get event, removed? (fd: %d size: %" PRId32
-                            " bufferSize: %zu capacity: %zu errno: %d)\n",
-                            device->fd, readSize, bufferSize, capacity, errno);
+                          " bufferSize: %zu capacity: %zu errno: %d)\n",
+                          device->fd, readSize, bufferSize, capacity, errno);
                     deviceChanged = true;
                     closeDeviceLocked(device);
                 } else if (readSize < 0) {
@@ -967,12 +976,12 @@
                 }
             } else if (eventItem.events & EPOLLHUP) {
                 ALOGI("Removing device %s due to epoll hang-up event.",
-                        device->identifier.name.c_str());
+                      device->identifier.name.c_str());
                 deviceChanged = true;
                 closeDeviceLocked(device);
             } else {
-                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
-                        eventItem.events, device->identifier.name.c_str());
+                ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
+                      device->identifier.name.c_str());
             }
         }
 
@@ -1067,7 +1076,7 @@
 
 void EventHub::scanDevicesLocked() {
     status_t result = scanDirLocked(DEVICE_PATH);
-    if(result < 0) {
+    if (result < 0) {
         ALOGE("scan dir failed for %s", DEVICE_PATH);
     }
     if (isV4lScanningEnabled()) {
@@ -1095,12 +1104,12 @@
 }
 
 static const int32_t GAMEPAD_KEYCODES[] = {
-        AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
-        AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
-        AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
-        AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
-        AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
-        AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
+        AKEYCODE_BUTTON_A,      AKEYCODE_BUTTON_B,      AKEYCODE_BUTTON_C,    //
+        AKEYCODE_BUTTON_X,      AKEYCODE_BUTTON_Y,      AKEYCODE_BUTTON_Z,    //
+        AKEYCODE_BUTTON_L1,     AKEYCODE_BUTTON_R1,                           //
+        AKEYCODE_BUTTON_L2,     AKEYCODE_BUTTON_R2,                           //
+        AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,                       //
+        AKEYCODE_BUTTON_START,  AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, //
 };
 
 status_t EventHub::registerFdForEpoll(int fd) {
@@ -1167,7 +1176,7 @@
         status_t result = unregisterFdFromEpoll(videoDevice.getFd());
         if (result != OK) {
             ALOGW("Could not remove video device fd from epoll for device: %s",
-                    videoDevice.getName().c_str());
+                  videoDevice.getName().c_str());
         }
     }
 }
@@ -1178,7 +1187,7 @@
     ALOGV("Opening device: %s", devicePath);
 
     int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
-    if(fd < 0) {
+    if (fd < 0) {
         ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
         return -1;
     }
@@ -1186,7 +1195,7 @@
     InputDeviceIdentifier identifier;
 
     // Get device name.
-    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
+    if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
         ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
@@ -1205,7 +1214,7 @@
 
     // Get device driver version.
     int driverVersion;
-    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
+    if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
         ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
         close(fd);
         return -1;
@@ -1213,7 +1222,7 @@
 
     // Get device identifier.
     struct input_id inputId;
-    if(ioctl(fd, EVIOCGID, &inputId)) {
+    if (ioctl(fd, EVIOCGID, &inputId)) {
         ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
         close(fd);
         return -1;
@@ -1224,16 +1233,16 @@
     identifier.version = inputId.version;
 
     // Get device physical location.
-    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
-        //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
+    if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
+        // fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
         identifier.location = buffer;
     }
 
     // Get device unique id.
-    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
-        //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
+    if (ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
+        // fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
         identifier.uniqueId = buffer;
@@ -1248,16 +1257,16 @@
 
     ALOGV("add device %d: %s\n", deviceId, devicePath);
     ALOGV("  bus:        %04x\n"
-         "  vendor      %04x\n"
-         "  product     %04x\n"
-         "  version     %04x\n",
-        identifier.bus, identifier.vendor, identifier.product, identifier.version);
+          "  vendor      %04x\n"
+          "  product     %04x\n"
+          "  version     %04x\n",
+          identifier.bus, identifier.vendor, identifier.product, identifier.version);
     ALOGV("  name:       \"%s\"\n", identifier.name.c_str());
     ALOGV("  location:   \"%s\"\n", identifier.location.c_str());
     ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.c_str());
     ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.c_str());
-    ALOGV("  driver:     v%d.%d.%d\n",
-        driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
+    ALOGV("  driver:     v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff,
+          driverVersion & 0xff);
 
     // Load the configuration file for the device.
     loadConfigurationLocked(device);
@@ -1273,21 +1282,21 @@
 
     // See if this is a keyboard.  Ignore everything in the button range except for
     // joystick and gamepad buttons which are handled like keyboards for the most part.
-    bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
-            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
-                    sizeof_bit_array(KEY_MAX + 1));
+    bool haveKeyboardKeys =
+            containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
+            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
+                                sizeof_bit_array(KEY_MAX + 1));
     bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
-                    sizeof_bit_array(BTN_MOUSE))
-            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
-                    sizeof_bit_array(BTN_DIGI));
+                                                  sizeof_bit_array(BTN_MOUSE)) ||
+            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
+                                sizeof_bit_array(BTN_DIGI));
     if (haveKeyboardKeys || haveGamepadButtons) {
         device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
     }
 
     // See if this is a cursor device such as a trackball or mouse.
-    if (test_bit(BTN_MOUSE, device->keyBitmask)
-            && test_bit(REL_X, device->relBitmask)
-            && test_bit(REL_Y, device->relBitmask)) {
+    if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
+        test_bit(REL_Y, device->relBitmask)) {
         device->classes |= INPUT_DEVICE_CLASS_CURSOR;
     }
 
@@ -1295,31 +1304,29 @@
     String8 deviceType = String8();
     if (device->configuration &&
         device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
-            if (!deviceType.compare(String8("rotaryEncoder"))) {
-                device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
-            }
+        if (!deviceType.compare(String8("rotaryEncoder"))) {
+            device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
+        }
     }
 
     // See if this is a touch pad.
     // Is this a new modern multi-touch driver?
-    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
-            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+    if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
+        test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
         // Some joysticks such as the PS3 controller report axes that conflict
         // with the ABS_MT range.  Try to confirm that the device really is
         // a touch screen.
         if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
             device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
         }
-    // Is this an old style single-touch driver?
-    } else if (test_bit(BTN_TOUCH, device->keyBitmask)
-            && test_bit(ABS_X, device->absBitmask)
-            && test_bit(ABS_Y, device->absBitmask)) {
+        // Is this an old style single-touch driver?
+    } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
+               test_bit(ABS_Y, device->absBitmask)) {
         device->classes |= INPUT_DEVICE_CLASS_TOUCH;
-    // Is this a BT stylus?
+        // Is this a BT stylus?
     } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
-                test_bit(BTN_TOUCH, device->keyBitmask))
-            && !test_bit(ABS_X, device->absBitmask)
-            && !test_bit(ABS_Y, device->absBitmask)) {
+                test_bit(BTN_TOUCH, device->keyBitmask)) &&
+               !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
         device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
         // Keyboard will try to claim some of the buttons but we really want to reserve those so we
         // can fuse it with the touch screen data, so just take them back. Note this means an
@@ -1333,8 +1340,8 @@
     if (haveGamepadButtons) {
         uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
         for (int i = 0; i <= ABS_MAX; i++) {
-            if (test_bit(i, device->absBitmask)
-                    && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+            if (test_bit(i, device->absBitmask) &&
+                (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
                 device->classes = assumedClasses;
                 break;
             }
@@ -1375,10 +1382,8 @@
     // Configure the keyboard, gamepad or virtual keyboard.
     if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
         // Register the keyboard as a built-in keyboard if it is eligible.
-        if (!keyMapStatus
-                && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
-                && isEligibleBuiltInKeyboard(device->identifier,
-                        device->configuration, &device->keyMap)) {
+        if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
+            isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
             mBuiltInKeyboardId = device->id;
         }
 
@@ -1389,15 +1394,15 @@
 
         // See if this device has a DPAD.
         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
-                hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
-                hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
-                hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
-                hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
+            hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
+            hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
+            hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
+            hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
             device->classes |= INPUT_DEVICE_CLASS_DPAD;
         }
 
         // See if this device has a gamepad.
-        for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
+        for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
             if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
                 device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
                 break;
@@ -1407,8 +1412,8 @@
 
     // If the device isn't recognized as something we handle, don't monitor it.
     if (device->classes == 0) {
-        ALOGV("Dropping device: id=%d, path='%s', name='%s'",
-                deviceId, devicePath, device->identifier.name.c_str());
+        ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+              device->identifier.name.c_str());
         delete device;
         return -1;
     }
@@ -1423,8 +1428,8 @@
         device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
     }
 
-    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
-            && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) &&
+        device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
         device->controllerNumber = getNextControllerNumberLocked(device);
         setLedForControllerLocked(device);
     }
@@ -1437,10 +1442,12 @@
             break;
         }
     }
-    mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
-            mUnattachedVideoDevices.end(),
-            [](const std::unique_ptr<TouchVideoDevice>& videoDevice){
-            return videoDevice == nullptr; }), mUnattachedVideoDevices.end());
+    mUnattachedVideoDevices
+            .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
+                                  [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+                                      return videoDevice == nullptr;
+                                  }),
+                   mUnattachedVideoDevices.end());
 
     if (registerDeviceForEpollLocked(device) != OK) {
         delete device;
@@ -1450,13 +1457,10 @@
     configureFd(device);
 
     ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
-            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
-         deviceId, fd, devicePath, device->identifier.name.c_str(),
-         device->classes,
-         device->configurationFile.c_str(),
-         device->keyMap.keyLayoutFile.c_str(),
-         device->keyMap.keyCharacterMapFile.c_str(),
-         toString(mBuiltInKeyboardId == deviceId));
+          "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
+          deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
+          device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
+          device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
 
     addDeviceLocked(device);
     return OK;
@@ -1468,8 +1472,8 @@
         // Disable kernel key repeat since we handle it ourselves
         unsigned int repeatRate[] = {0, 0};
         if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
-            ALOGW("Unable to disable kernel key repeat for %s: %s",
-                  device->path.c_str(), strerror(errno));
+            ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(),
+                  strerror(errno));
         }
     }
 
@@ -1503,7 +1507,7 @@
     // Couldn't find a matching input device, so just add it to a temporary holding queue.
     // A matching input device may appear later.
     ALOGI("Adding video device %s to list of unattached video devices",
-            videoDevice->getName().c_str());
+          videoDevice->getName().c_str());
     mUnattachedVideoDevices.push_back(std::move(videoDevice));
 }
 
@@ -1560,12 +1564,10 @@
     identifier.uniqueId = "<virtual>";
     assignDescriptorLocked(identifier);
 
-    Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
-            identifier);
-    device->classes = INPUT_DEVICE_CLASS_KEYBOARD
-            | INPUT_DEVICE_CLASS_ALPHAKEY
-            | INPUT_DEVICE_CLASS_DPAD
-            | INPUT_DEVICE_CLASS_VIRTUAL;
+    Device* device =
+            new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
+    device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY |
+            INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL;
     loadKeyMapLocked(device);
     addDeviceLocked(device);
 }
@@ -1581,14 +1583,14 @@
             device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
     if (device->configurationFile.empty()) {
         ALOGD("No input device configuration file found for device '%s'.",
-                device->identifier.name.c_str());
+              device->identifier.name.c_str());
     } else {
         status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
-                &device->configuration);
+                                            &device->configuration);
         if (status) {
             ALOGE("Error loading input device configuration file for device '%s'.  "
-                    "Using default configuration.",
-                    device->identifier.name.c_str());
+                  "Using default configuration.",
+                  device->identifier.name.c_str());
         }
     }
 }
@@ -1632,7 +1634,7 @@
 int32_t EventHub::getNextControllerNumberLocked(Device* device) {
     if (mControllerNumbers.isFull()) {
         ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
-                device->identifier.name.c_str());
+              device->identifier.name.c_str());
         return 0;
     }
     // Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1642,7 +1644,7 @@
 
 void EventHub::releaseControllerNumberLocked(Device* device) {
     int32_t num = device->controllerNumber;
-    device->controllerNumber= 0;
+    device->controllerNumber = 0;
     if (num == 0) {
         return;
     }
@@ -1663,7 +1665,7 @@
     std::vector<int32_t> scanCodes;
     device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
     const size_t N = scanCodes.size();
-    for (size_t i=0; i<N && i<=KEY_MAX; i++) {
+    for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
         int32_t sc = scanCodes[i];
         if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
             return true;
@@ -1679,8 +1681,8 @@
     }
 
     int32_t scanCode;
-    if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
-        if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
+    if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
+        if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
             *outScanCode = scanCode;
             return NO_ERROR;
         }
@@ -1688,7 +1690,7 @@
     return NAME_NOT_FOUND;
 }
 
-void EventHub::closeDeviceByPathLocked(const char *devicePath) {
+void EventHub::closeDeviceByPathLocked(const char* devicePath) {
     Device* device = getDeviceByPathLocked(devicePath);
     if (device) {
         closeDeviceLocked(device);
@@ -1713,10 +1715,13 @@
             return;
         }
     }
-    mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
-            mUnattachedVideoDevices.end(), [&devicePath](
-            const std::unique_ptr<TouchVideoDevice>& videoDevice) {
-            return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end());
+    mUnattachedVideoDevices
+            .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
+                                  [&devicePath](
+                                          const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+                                      return videoDevice->getPath() == devicePath;
+                                  }),
+                   mUnattachedVideoDevices.end());
 }
 
 void EventHub::closeAllDevicesLocked() {
@@ -1727,13 +1732,12 @@
 }
 
 void EventHub::closeDeviceLocked(Device* device) {
-    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x",
-         device->path.c_str(), device->identifier.name.c_str(), device->id,
-         device->fd, device->classes);
+    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
+          device->identifier.name.c_str(), device->id, device->fd, device->classes);
 
     if (device->id == mBuiltInKeyboardId) {
         ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
-                device->path.c_str(), mBuiltInKeyboardId);
+              device->path.c_str(), mBuiltInKeyboardId);
         mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
     }
 
@@ -1751,7 +1755,7 @@
     // Unlink for opening devices list if it is present.
     Device* pred = nullptr;
     bool found = false;
-    for (Device* entry = mOpeningDevices; entry != nullptr; ) {
+    for (Device* entry = mOpeningDevices; entry != nullptr;) {
         if (entry == device) {
             found = true;
             break;
@@ -1783,30 +1787,28 @@
     char event_buf[512];
     int event_size;
     int event_pos = 0;
-    struct inotify_event *event;
+    struct inotify_event* event;
 
     ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
     res = read(mINotifyFd, event_buf, sizeof(event_buf));
-    if(res < (int)sizeof(*event)) {
-        if(errno == EINTR)
-            return 0;
+    if (res < (int)sizeof(*event)) {
+        if (errno == EINTR) return 0;
         ALOGW("could not get event, %s\n", strerror(errno));
         return -1;
     }
 
-    while(res >= (int)sizeof(*event)) {
-        event = (struct inotify_event *)(event_buf + event_pos);
-        if(event->len) {
+    while (res >= (int)sizeof(*event)) {
+        event = (struct inotify_event*)(event_buf + event_pos);
+        if (event->len) {
             if (event->wd == mInputWd) {
                 std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
-                if(event->mask & IN_CREATE) {
+                if (event->mask & IN_CREATE) {
                     openDeviceLocked(filename.c_str());
                 } else {
                     ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
                     closeDeviceByPathLocked(filename.c_str());
                 }
-            }
-            else if (event->wd == mVideoWd) {
+            } else if (event->wd == mVideoWd) {
                 if (isV4lTouchNode(event->name)) {
                     std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
                     if (event->mask & IN_CREATE) {
@@ -1816,8 +1818,7 @@
                         closeVideoDeviceByPathLocked(filename);
                     }
                 }
-            }
-            else {
+            } else {
                 LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
             }
         }
@@ -1828,22 +1829,19 @@
     return 0;
 }
 
-status_t EventHub::scanDirLocked(const char *dirname)
-{
+status_t EventHub::scanDirLocked(const char* dirname) {
     char devname[PATH_MAX];
-    char *filename;
-    DIR *dir;
-    struct dirent *de;
+    char* filename;
+    DIR* dir;
+    struct dirent* de;
     dir = opendir(dirname);
-    if(dir == nullptr)
-        return -1;
+    if (dir == nullptr) return -1;
     strcpy(devname, dirname);
     filename = devname + strlen(devname);
     *filename++ = '/';
-    while((de = readdir(dir))) {
-        if(de->d_name[0] == '.' &&
-           (de->d_name[1] == '\0' ||
-            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+    while ((de = readdir(dir))) {
+        if (de->d_name[0] == '.' &&
+            (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
             continue;
         strcpy(filename, de->d_name);
         openDeviceLocked(devname);
@@ -1855,17 +1853,16 @@
 /**
  * Look for all dirname/v4l-touch* devices, and open them.
  */
-status_t EventHub::scanVideoDirLocked(const std::string& dirname)
-{
+status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
     DIR* dir;
     struct dirent* de;
     dir = opendir(dirname.c_str());
-    if(!dir) {
+    if (!dir) {
         ALOGE("Could not open video directory %s", dirname.c_str());
         return BAD_VALUE;
     }
 
-    while((de = readdir(dir))) {
+    while ((de = readdir(dir))) {
         const char* name = de->d_name;
         if (isV4lTouchNode(name)) {
             ALOGI("Found touch video device %s", name);
@@ -1897,10 +1894,10 @@
             const Device* device = mDevices.valueAt(i);
             if (mBuiltInKeyboardId == device->id) {
                 dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
-                        device->id, device->identifier.name.c_str());
+                                     device->id, device->identifier.name.c_str());
             } else {
                 dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
-                        device->identifier.name.c_str());
+                                     device->identifier.name.c_str());
             }
             dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
             dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
@@ -1910,17 +1907,17 @@
             dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
             dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
             dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
-                    "product=0x%04x, version=0x%04x\n",
-                    device->identifier.bus, device->identifier.vendor,
-                    device->identifier.product, device->identifier.version);
+                                         "product=0x%04x, version=0x%04x\n",
+                                 device->identifier.bus, device->identifier.vendor,
+                                 device->identifier.product, device->identifier.version);
             dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
-                    device->keyMap.keyLayoutFile.c_str());
+                                 device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
-                    device->keyMap.keyCharacterMapFile.c_str());
+                                 device->keyMap.keyCharacterMapFile.c_str());
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
-                    device->configurationFile.c_str());
+                                 device->configurationFile.c_str());
             dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
-                    toString(device->overlayKeyMap != nullptr));
+                                 toString(device->overlayKeyMap != nullptr));
             dump += INDENT3 "VideoDevice: ";
             if (device->videoDevice) {
                 dump += device->videoDevice->dump() + "\n";
@@ -1945,5 +1942,4 @@
     mLock.unlock();
 }
 
-
 }; // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
new file mode 100644
index 0000000..7fed61f
--- /dev/null
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputDevice.h"
+
+#include "InputMapper.h"
+
+namespace android {
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+                         int32_t controllerNumber, const InputDeviceIdentifier& identifier,
+                         uint32_t classes)
+      : mContext(context),
+        mId(id),
+        mGeneration(generation),
+        mControllerNumber(controllerNumber),
+        mIdentifier(identifier),
+        mClasses(classes),
+        mSources(0),
+        mIsExternal(false),
+        mHasMic(false),
+        mDropUntilNextSync(false) {}
+
+InputDevice::~InputDevice() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        delete mMappers[i];
+    }
+    mMappers.clear();
+}
+
+bool InputDevice::isEnabled() {
+    return getEventHub()->isDeviceEnabled(mId);
+}
+
+void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+              "but the corresponding viewport is not found",
+              getName().c_str(), *mAssociatedDisplayPort);
+        enabled = false;
+    }
+
+    if (isEnabled() == enabled) {
+        return;
+    }
+
+    if (enabled) {
+        getEventHub()->enableDevice(mId);
+        reset(when);
+    } else {
+        reset(when);
+        getEventHub()->disableDevice(mId);
+    }
+    // Must change generation to flag this device as changed
+    bumpGeneration();
+}
+
+void InputDevice::dump(std::string& dump) {
+    InputDeviceInfo deviceInfo;
+    getDeviceInfo(&deviceInfo);
+
+    dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
+                         deviceInfo.getDisplayName().c_str());
+    dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
+    dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+    dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+    if (mAssociatedDisplayPort) {
+        dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+    } else {
+        dump += "<none>\n";
+    }
+    dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
+    dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+    dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+
+    const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
+    if (!ranges.empty()) {
+        dump += INDENT2 "Motion Ranges:\n";
+        for (size_t i = 0; i < ranges.size(); i++) {
+            const InputDeviceInfo::MotionRange& range = ranges[i];
+            const char* label = getAxisLabel(range.axis);
+            char name[32];
+            if (label) {
+                strncpy(name, label, sizeof(name));
+                name[sizeof(name) - 1] = '\0';
+            } else {
+                snprintf(name, sizeof(name), "%d", range.axis);
+            }
+            dump += StringPrintf(INDENT3
+                                 "%s: source=0x%08x, "
+                                 "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
+                                 name, range.source, range.min, range.max, range.flat, range.fuzz,
+                                 range.resolution);
+        }
+    }
+
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->dump(dump);
+    }
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+    mMappers.push_back(mapper);
+}
+
+void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
+                            uint32_t changes) {
+    mSources = 0;
+
+    if (!isIgnored()) {
+        if (!changes) { // first time only
+            mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
+        }
+
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
+            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+                sp<KeyCharacterMap> keyboardLayout =
+                        mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
+                if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
+                    bumpGeneration();
+                }
+            }
+        }
+
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
+            if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+                std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
+                if (mAlias != alias) {
+                    mAlias = alias;
+                    bumpGeneration();
+                }
+            }
+        }
+
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
+            auto it = config->disabledDevices.find(mId);
+            bool enabled = it == config->disabledDevices.end();
+            setEnabled(enabled, when);
+        }
+
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            // In most situations, no port will be specified.
+            mAssociatedDisplayPort = std::nullopt;
+            mAssociatedViewport = std::nullopt;
+            // Find the display port that corresponds to the current input port.
+            const std::string& inputPort = mIdentifier.location;
+            if (!inputPort.empty()) {
+                const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+                const auto& displayPort = ports.find(inputPort);
+                if (displayPort != ports.end()) {
+                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
+                }
+            }
+
+            // If the device was explicitly disabled by the user, it would be present in the
+            // "disabledDevices" list. If it is associated with a specific display, and it was not
+            // explicitly disabled, then enable/disable the device based on whether we can find the
+            // corresponding viewport.
+            bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
+            if (mAssociatedDisplayPort) {
+                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
+                if (!mAssociatedViewport) {
+                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                          "but the corresponding viewport is not found.",
+                          getName().c_str(), *mAssociatedDisplayPort);
+                    enabled = false;
+                }
+            }
+
+            if (changes) {
+                // For first-time configuration, only allow device to be disabled after mappers have
+                // finished configuring. This is because we need to read some of the properties from
+                // the device's open fd.
+                setEnabled(enabled, when);
+            }
+        }
+
+        for (InputMapper* mapper : mMappers) {
+            mapper->configure(when, config, changes);
+            mSources |= mapper->getSources();
+        }
+
+        // If a device is just plugged but it might be disabled, we need to update some info like
+        // axis range of touch from each InputMapper first, then disable it.
+        if (!changes) {
+            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+        }
+    }
+}
+
+void InputDevice::reset(nsecs_t when) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->reset(when);
+    }
+
+    mContext->updateGlobalMetaState();
+
+    notifyReset(when);
+}
+
+void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+    // Process all of the events in order for each mapper.
+    // We cannot simply ask each mapper to process them in bulk because mappers may
+    // have side-effects that must be interleaved.  For example, joystick movement events and
+    // gamepad button presses are handled by different mappers but they should be dispatched
+    // in the order received.
+    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
+#if DEBUG_RAW_EVENTS
+        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
+              rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
+#endif
+
+        if (mDropUntilNextSync) {
+            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+                mDropUntilNextSync = false;
+#if DEBUG_RAW_EVENTS
+                ALOGD("Recovered from input event buffer overrun.");
+#endif
+            } else {
+#if DEBUG_RAW_EVENTS
+                ALOGD("Dropped input event while waiting for next input sync.");
+#endif
+            }
+        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
+            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
+            mDropUntilNextSync = true;
+            reset(rawEvent->when);
+        } else {
+            for (InputMapper* mapper : mMappers) {
+                mapper->process(rawEvent);
+            }
+        }
+        --count;
+    }
+}
+
+void InputDevice::timeoutExpired(nsecs_t when) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->timeoutExpired(when);
+    }
+}
+
+void InputDevice::updateExternalStylusState(const StylusState& state) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->updateExternalStylusState(state);
+    }
+}
+
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+    outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
+                              mHasMic);
+    for (InputMapper* mapper : mMappers) {
+        mapper->populateDeviceInfo(outDeviceInfo);
+    }
+}
+
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState);
+}
+
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return getState(sourceMask, scanCode, &InputMapper::getScanCodeState);
+}
+
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return getState(sourceMask, switchCode, &InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+    int32_t result = AKEY_STATE_UNKNOWN;
+    for (InputMapper* mapper : mMappers) {
+        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+            // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+            // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
+            int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
+            if (currentResult >= AKEY_STATE_DOWN) {
+                return currentResult;
+            } else if (currentResult == AKEY_STATE_UP) {
+                result = currentResult;
+            }
+        }
+    }
+    return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                        const int32_t* keyCodes, uint8_t* outFlags) {
+    bool result = false;
+    for (InputMapper* mapper : mMappers) {
+        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+        }
+    }
+    return result;
+}
+
+void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                          int32_t token) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->vibrate(pattern, patternSize, repeat, token);
+    }
+}
+
+void InputDevice::cancelVibrate(int32_t token) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->cancelVibrate(token);
+    }
+}
+
+void InputDevice::cancelTouch(nsecs_t when) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->cancelTouch(when);
+    }
+}
+
+int32_t InputDevice::getMetaState() {
+    int32_t result = 0;
+    for (InputMapper* mapper : mMappers) {
+        result |= mapper->getMetaState();
+    }
+    return result;
+}
+
+void InputDevice::updateMetaState(int32_t keyCode) {
+    for (InputMapper* mapper : mMappers) {
+        mapper->updateMetaState(keyCode);
+    }
+}
+
+void InputDevice::fadePointer() {
+    for (InputMapper* mapper : mMappers) {
+        mapper->fadePointer();
+    }
+}
+
+void InputDevice::bumpGeneration() {
+    mGeneration = mContext->bumpGeneration();
+}
+
+void InputDevice::notifyReset(nsecs_t when) {
+    NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
+    mContext->getListener()->notifyDeviceReset(&args);
+}
+
+std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+    // Check if we had associated to the specific display.
+    if (mAssociatedViewport) {
+        return mAssociatedViewport->displayId;
+    }
+
+    // No associated display port, check if some InputMapper is associated.
+    for (InputMapper* mapper : mMappers) {
+        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
+        if (associatedDisplayId) {
+            return associatedDisplayId;
+        }
+    }
+
+    return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
new file mode 100644
index 0000000..e57604c
--- /dev/null
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputReader.h"
+
+#include "CursorInputMapper.h"
+#include "ExternalStylusInputMapper.h"
+#include "InputReaderContext.h"
+#include "JoystickInputMapper.h"
+#include "KeyboardInputMapper.h"
+#include "MultiTouchInputMapper.h"
+#include "RotaryEncoderInputMapper.h"
+#include "SingleTouchInputMapper.h"
+#include "SwitchInputMapper.h"
+#include "VibratorInputMapper.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
+
+
+using android::base::StringPrintf;
+
+namespace android {
+
+InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
+                         const sp<InputReaderPolicyInterface>& policy,
+                         const sp<InputListenerInterface>& listener)
+      : mContext(this),
+        mEventHub(eventHub),
+        mPolicy(policy),
+        mNextSequenceNum(1),
+        mGlobalMetaState(0),
+        mGeneration(1),
+        mDisableVirtualKeysTimeout(LLONG_MIN),
+        mNextTimeout(LLONG_MAX),
+        mConfigurationChangesToRefresh(0) {
+    mQueuedListener = new QueuedInputListener(listener);
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        refreshConfigurationLocked(0);
+        updateGlobalMetaStateLocked();
+    } // release lock
+}
+
+InputReader::~InputReader() {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        delete mDevices.valueAt(i);
+    }
+}
+
+void InputReader::loopOnce() {
+    int32_t oldGeneration;
+    int32_t timeoutMillis;
+    bool inputDevicesChanged = false;
+    std::vector<InputDeviceInfo> inputDevices;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        oldGeneration = mGeneration;
+        timeoutMillis = -1;
+
+        uint32_t changes = mConfigurationChangesToRefresh;
+        if (changes) {
+            mConfigurationChangesToRefresh = 0;
+            timeoutMillis = 0;
+            refreshConfigurationLocked(changes);
+        } else if (mNextTimeout != LLONG_MAX) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
+        }
+    } // release lock
+
+    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+        mReaderIsAliveCondition.broadcast();
+
+        if (count) {
+            processEventsLocked(mEventBuffer, count);
+        }
+
+        if (mNextTimeout != LLONG_MAX) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            if (now >= mNextTimeout) {
+#if DEBUG_RAW_EVENTS
+                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+#endif
+                mNextTimeout = LLONG_MAX;
+                timeoutExpiredLocked(now);
+            }
+        }
+
+        if (oldGeneration != mGeneration) {
+            inputDevicesChanged = true;
+            getInputDevicesLocked(inputDevices);
+        }
+    } // release lock
+
+    // Send out a message that the describes the changed input devices.
+    if (inputDevicesChanged) {
+        mPolicy->notifyInputDevicesChanged(inputDevices);
+    }
+
+    // Flush queued events out to the listener.
+    // This must happen outside of the lock because the listener could potentially call
+    // back into the InputReader's methods, such as getScanCodeState, or become blocked
+    // on another thread similarly waiting to acquire the InputReader lock thereby
+    // resulting in a deadlock.  This situation is actually quite plausible because the
+    // listener is actually the input dispatcher, which calls into the window manager,
+    // which occasionally calls into the input reader.
+    mQueuedListener->flush();
+}
+
+void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+    for (const RawEvent* rawEvent = rawEvents; count;) {
+        int32_t type = rawEvent->type;
+        size_t batchSize = 1;
+        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+            int32_t deviceId = rawEvent->deviceId;
+            while (batchSize < count) {
+                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
+                    rawEvent[batchSize].deviceId != deviceId) {
+                    break;
+                }
+                batchSize += 1;
+            }
+#if DEBUG_RAW_EVENTS
+            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
+#endif
+            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
+        } else {
+            switch (rawEvent->type) {
+                case EventHubInterface::DEVICE_ADDED:
+                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
+                    break;
+                case EventHubInterface::DEVICE_REMOVED:
+                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
+                    break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    handleConfigurationChangedLocked(rawEvent->when);
+                    break;
+                default:
+                    ALOG_ASSERT(false); // can't happen
+                    break;
+            }
+        }
+        count -= batchSize;
+        rawEvent += batchSize;
+    }
+}
+
+void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+        return;
+    }
+
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
+    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
+
+    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+    device->configure(when, &mConfig, 0);
+    device->reset(when);
+
+    if (device->isIgnored()) {
+        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
+              identifier.name.c_str());
+    } else {
+        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(),
+              device->getSources());
+    }
+
+    mDevices.add(deviceId, device);
+    bumpGenerationLocked();
+
+    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        notifyExternalStylusPresenceChanged();
+    }
+}
+
+void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
+    InputDevice* device = nullptr;
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+        return;
+    }
+
+    device = mDevices.valueAt(deviceIndex);
+    mDevices.removeItemsAt(deviceIndex, 1);
+    bumpGenerationLocked();
+
+    if (device->isIgnored()) {
+        ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(),
+              device->getName().c_str());
+    } else {
+        ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(),
+              device->getName().c_str(), device->getSources());
+    }
+
+    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        notifyExternalStylusPresenceChanged();
+    }
+
+    device->reset(when);
+    delete device;
+}
+
+InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
+                                             const InputDeviceIdentifier& identifier,
+                                             uint32_t classes) {
+    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
+                                          controllerNumber, identifier, classes);
+
+    // External devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
+        device->setExternal(true);
+    }
+
+    // Devices with mics.
+    if (classes & INPUT_DEVICE_CLASS_MIC) {
+        device->setMic(true);
+    }
+
+    // Switch-like devices.
+    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+        device->addMapper(new SwitchInputMapper(device));
+    }
+
+    // Scroll wheel-like devices.
+    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+        device->addMapper(new RotaryEncoderInputMapper(device));
+    }
+
+    // Vibrator-like devices.
+    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+        device->addMapper(new VibratorInputMapper(device));
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSource = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+        keyboardSource |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSource != 0) {
+        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
+    }
+
+    // Cursor-like devices.
+    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+        device->addMapper(new CursorInputMapper(device));
+    }
+
+    // Touchscreens and touchpad devices.
+    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+        device->addMapper(new MultiTouchInputMapper(device));
+    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+        device->addMapper(new SingleTouchInputMapper(device));
+    }
+
+    // Joystick-like devices.
+    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+        device->addMapper(new JoystickInputMapper(device));
+    }
+
+    // External stylus-like devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        device->addMapper(new ExternalStylusInputMapper(device));
+    }
+
+    return device;
+}
+
+void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
+                                               size_t count) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
+        return;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (device->isIgnored()) {
+        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
+        return;
+    }
+
+    device->process(rawEvents, count);
+}
+
+void InputReader::timeoutExpiredLocked(nsecs_t when) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (!device->isIgnored()) {
+            device->timeoutExpired(when);
+        }
+    }
+}
+
+void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
+    // Reset global meta state because it depends on the list of all configured devices.
+    updateGlobalMetaStateLocked();
+
+    // Enqueue configuration changed.
+    NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
+    mQueuedListener->notifyConfigurationChanged(&args);
+}
+
+void InputReader::refreshConfigurationLocked(uint32_t changes) {
+    mPolicy->getReaderConfiguration(&mConfig);
+    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
+
+    if (changes) {
+        ALOGI("Reconfiguring input devices, changes=%s",
+              InputReaderConfiguration::changesToString(changes).c_str());
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+            mEventHub->requestReopenDevices();
+        } else {
+            for (size_t i = 0; i < mDevices.size(); i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                device->configure(now, &mConfig, changes);
+            }
+        }
+    }
+}
+
+void InputReader::updateGlobalMetaStateLocked() {
+    mGlobalMetaState = 0;
+
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        mGlobalMetaState |= device->getMetaState();
+    }
+}
+
+int32_t InputReader::getGlobalMetaStateLocked() {
+    return mGlobalMetaState;
+}
+
+void InputReader::notifyExternalStylusPresenceChanged() {
+    refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+}
+
+void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+            InputDeviceInfo info;
+            device->getDeviceInfo(&info);
+            outDevices.push_back(info);
+        }
+    }
+}
+
+void InputReader::dispatchExternalStylusState(const StylusState& state) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        device->updateExternalStylusState(state);
+    }
+}
+
+void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
+    mDisableVirtualKeysTimeout = time;
+}
+
+bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
+                                             int32_t scanCode) {
+    if (now < mDisableVirtualKeysTimeout) {
+        ALOGI("Dropping virtual key from device %s because virtual keys are "
+              "temporarily disabled for the next %0.3fms.  keyCode=%d, scanCode=%d",
+              device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode,
+              scanCode);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void InputReader::fadePointerLocked() {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        device->fadePointer();
+    }
+}
+
+void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
+    if (when < mNextTimeout) {
+        mNextTimeout = when;
+        mEventHub->wake();
+    }
+}
+
+int32_t InputReader::bumpGenerationLocked() {
+    return ++mGeneration;
+}
+
+void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
+    AutoMutex _l(mLock);
+    getInputDevicesLocked(outInputDevices);
+}
+
+void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
+    outInputDevices.clear();
+
+    size_t numDevices = mDevices.size();
+    for (size_t i = 0; i < numDevices; i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (!device->isIgnored()) {
+            InputDeviceInfo info;
+            device->getDeviceInfo(&info);
+            outInputDevices.push_back(info);
+        }
+    }
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
+                                    GetStateFunc getStateFunc) {
+    int32_t result = AKEY_STATE_UNKNOWN;
+    if (deviceId >= 0) {
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex >= 0) {
+            InputDevice* device = mDevices.valueAt(deviceIndex);
+            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result = (device->*getStateFunc)(sourceMask, code);
+            }
+        }
+    } else {
+        size_t numDevices = mDevices.size();
+        for (size_t i = 0; i < numDevices; i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+                // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
+                int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
+                if (currentResult >= AKEY_STATE_DOWN) {
+                    return currentResult;
+                } else if (currentResult == AKEY_STATE_UP) {
+                    result = currentResult;
+                }
+            }
+        }
+    }
+    return result;
+}
+
+void InputReader::toggleCapsLockState(int32_t deviceId) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
+        return;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (device->isIgnored()) {
+        return;
+    }
+
+    device->updateMetaState(AKEYCODE_CAPS_LOCK);
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+                          const int32_t* keyCodes, uint8_t* outFlags) {
+    AutoMutex _l(mLock);
+
+    memset(outFlags, 0, numCodes);
+    return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
+                                              size_t numCodes, const int32_t* keyCodes,
+                                              uint8_t* outFlags) {
+    bool result = false;
+    if (deviceId >= 0) {
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex >= 0) {
+            InputDevice* device = mDevices.valueAt(deviceIndex);
+            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+            }
+        }
+    } else {
+        size_t numDevices = mDevices.size();
+        for (size_t i = 0; i < numDevices; i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+            }
+        }
+    }
+    return result;
+}
+
+void InputReader::requestRefreshConfiguration(uint32_t changes) {
+    AutoMutex _l(mLock);
+
+    if (changes) {
+        bool needWake = !mConfigurationChangesToRefresh;
+        mConfigurationChangesToRefresh |= changes;
+
+        if (needWake) {
+            mEventHub->wake();
+        }
+    }
+}
+
+void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+                          ssize_t repeat, int32_t token) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        device->vibrate(pattern, patternSize, repeat, token);
+    }
+}
+
+void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        device->cancelVibrate(token);
+    }
+}
+
+bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        InputDevice* device = mDevices.valueAt(deviceIndex);
+        return device->isEnabled();
+    }
+    ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+    return false;
+}
+
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+        return false;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (!device->isEnabled()) {
+        ALOGW("Ignoring disabled device %s", device->getName().c_str());
+        return false;
+    }
+
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
+    // No associated display. By default, can dispatch to all displays.
+    if (!associatedDisplayId) {
+        return true;
+    }
+
+    if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
+        return true;
+    }
+
+    return *associatedDisplayId == displayId;
+}
+
+void InputReader::dump(std::string& dump) {
+    AutoMutex _l(mLock);
+
+    mEventHub->dump(dump);
+    dump += "\n";
+
+    dump += "Input Reader State:\n";
+
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        mDevices.valueAt(i)->dump(dump);
+    }
+
+    dump += INDENT "Configuration:\n";
+    dump += INDENT2 "ExcludedDeviceNames: [";
+    for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
+        if (i != 0) {
+            dump += ", ";
+        }
+        dump += mConfig.excludedDeviceNames[i];
+    }
+    dump += "]\n";
+    dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
+                         mConfig.virtualKeyQuietTime * 0.000001f);
+
+    dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: "
+                                 "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, "
+                                 "acceleration=%0.3f\n",
+                         mConfig.pointerVelocityControlParameters.scale,
+                         mConfig.pointerVelocityControlParameters.lowThreshold,
+                         mConfig.pointerVelocityControlParameters.highThreshold,
+                         mConfig.pointerVelocityControlParameters.acceleration);
+
+    dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: "
+                                 "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, "
+                                 "acceleration=%0.3f\n",
+                         mConfig.wheelVelocityControlParameters.scale,
+                         mConfig.wheelVelocityControlParameters.lowThreshold,
+                         mConfig.wheelVelocityControlParameters.highThreshold,
+                         mConfig.wheelVelocityControlParameters.acceleration);
+
+    dump += StringPrintf(INDENT2 "PointerGesture:\n");
+    dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(mConfig.pointerGesturesEnabled));
+    dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n",
+                         mConfig.pointerGestureQuietInterval * 0.000001f);
+    dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
+                         mConfig.pointerGestureDragMinSwitchSpeed);
+    dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n",
+                         mConfig.pointerGestureTapInterval * 0.000001f);
+    dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n",
+                         mConfig.pointerGestureTapDragInterval * 0.000001f);
+    dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", mConfig.pointerGestureTapSlop);
+    dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
+                         mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
+    dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+                         mConfig.pointerGestureMultitouchMinDistance);
+    dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
+                         mConfig.pointerGestureSwipeTransitionAngleCosine);
+    dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
+                         mConfig.pointerGestureSwipeMaxWidthRatio);
+    dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n",
+                         mConfig.pointerGestureMovementSpeedRatio);
+    dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", mConfig.pointerGestureZoomSpeedRatio);
+
+    dump += INDENT3 "Viewports:\n";
+    mConfig.dump(dump);
+}
+
+void InputReader::monitor() {
+    // Acquire and release the lock to ensure that the reader has not deadlocked.
+    mLock.lock();
+    mEventHub->wake();
+    mReaderIsAliveCondition.wait(mLock);
+    mLock.unlock();
+
+    // Check the EventHub
+    mEventHub->monitor();
+}
+
+// --- InputReader::ContextImpl ---
+
+InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {}
+
+void InputReader::ContextImpl::updateGlobalMetaState() {
+    // lock is already held by the input loop
+    mReader->updateGlobalMetaStateLocked();
+}
+
+int32_t InputReader::ContextImpl::getGlobalMetaState() {
+    // lock is already held by the input loop
+    return mReader->getGlobalMetaStateLocked();
+}
+
+void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
+    // lock is already held by the input loop
+    mReader->disableVirtualKeysUntilLocked(time);
+}
+
+bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device,
+                                                    int32_t keyCode, int32_t scanCode) {
+    // lock is already held by the input loop
+    return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
+}
+
+void InputReader::ContextImpl::fadePointer() {
+    // lock is already held by the input loop
+    mReader->fadePointerLocked();
+}
+
+void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
+    // lock is already held by the input loop
+    mReader->requestTimeoutAtTimeLocked(when);
+}
+
+int32_t InputReader::ContextImpl::bumpGeneration() {
+    // lock is already held by the input loop
+    return mReader->bumpGenerationLocked();
+}
+
+void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
+    // lock is already held by whatever called refreshConfigurationLocked
+    mReader->getExternalStylusDevicesLocked(outDevices);
+}
+
+void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
+    mReader->dispatchExternalStylusState(state);
+}
+
+InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
+    return mReader->mPolicy.get();
+}
+
+InputListenerInterface* InputReader::ContextImpl::getListener() {
+    return mReader->mQueuedListener.get();
+}
+
+EventHubInterface* InputReader::ContextImpl::getEventHub() {
+    return mReader->mEventHub.get();
+}
+
+uint32_t InputReader::ContextImpl::getNextSequenceNum() {
+    return (mReader->mNextSequenceNum)++;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
similarity index 81%
rename from services/inputflinger/InputReaderFactory.cpp
rename to services/inputflinger/reader/InputReaderFactory.cpp
index 072499b..a897141 100644
--- a/services/inputflinger/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -15,13 +15,13 @@
  */
 
 #include "InputReaderFactory.h"
+
 #include "InputReader.h"
 
 namespace android {
 
-sp<InputReaderInterface> createInputReader(
-        const sp<InputReaderPolicyInterface>& policy,
-        const sp<InputListenerInterface>& listener) {
+sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
+                                           const sp<InputListenerInterface>& listener) {
     return new InputReader(std::make_unique<EventHub>(), policy, listener);
 }
 
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
new file mode 100644
index 0000000..827e31a
--- /dev/null
+++ b/services/inputflinger/reader/Macros.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_MACROS_H
+#define _UI_INPUTREADER_MACROS_H
+
+#define LOG_TAG "InputReader"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages for each raw event received from the EventHub.
+#define DEBUG_RAW_EVENTS 0
+
+// Log debug messages about touch screen filtering hacks.
+#define DEBUG_HACKS 0
+
+// Log debug messages about virtual key processing.
+#define DEBUG_VIRTUAL_KEYS 0
+
+// Log debug messages about pointers.
+#define DEBUG_POINTERS 0
+
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
+// Log debug messages about gesture detection.
+#define DEBUG_GESTURES 0
+
+// Log debug messages about the vibrator.
+#define DEBUG_VIBRATOR 0
+
+// Log debug messages about fusing stylus data.
+#define DEBUG_STYLUS_FUSION 0
+
+#define INDENT "  "
+#define INDENT2 "    "
+#define INDENT3 "      "
+#define INDENT4 "        "
+#define INDENT5 "          "
+
+#include <input/Input.h>
+
+namespace android {
+
+// --- Static Functions ---
+
+template <typename T>
+inline static T abs(const T& value) {
+    return value < 0 ? -value : value;
+}
+
+template <typename T>
+inline static T min(const T& a, const T& b) {
+    return a < b ? a : b;
+}
+
+inline static float avg(float x, float y) {
+    return (x + y) / 2;
+}
+
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+    return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file
diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
similarity index 84%
rename from services/inputflinger/TouchVideoDevice.cpp
rename to services/inputflinger/reader/TouchVideoDevice.cpp
index 19c1313..c075078 100644
--- a/services/inputflinger/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -37,10 +37,13 @@
 namespace android {
 
 TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
-        uint32_t height, uint32_t width,
-        const std::array<const int16_t*, NUM_BUFFERS>& readLocations) :
-        mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)),
-        mHeight(height), mWidth(width),
+                                   uint32_t height, uint32_t width,
+                                   const std::array<const int16_t*, NUM_BUFFERS>& readLocations)
+      : mFd(fd),
+        mName(std::move(name)),
+        mPath(std::move(devicePath)),
+        mHeight(height),
+        mWidth(width),
         mReadLocations(readLocations) {
     mFrames.reserve(MAX_QUEUE_SIZE);
 };
@@ -60,11 +63,11 @@
     }
     if (!(cap.capabilities & V4L2_CAP_TOUCH)) {
         ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. "
-                "Make sure device specifies V4L2_CAP_TOUCH");
+              "Make sure device specifies V4L2_CAP_TOUCH");
         return nullptr;
     }
-    ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i",
-            cap.driver, cap.card, cap.bus_info, cap.version);
+    ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", cap.driver,
+          cap.card, cap.bus_info, cap.version);
     std::string name = reinterpret_cast<const char*>(cap.card);
 
     struct v4l2_input v4l2_input_struct;
@@ -77,7 +80,7 @@
 
     if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) {
         ALOGE("Video device does not provide touch data. "
-                "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
+              "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
         return nullptr;
     }
 
@@ -120,14 +123,14 @@
             return nullptr;
         }
         if (buf.length != height * width * sizeof(int16_t)) {
-            ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")",
-                    buf.length, buf.m.offset);
+            ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", buf.length,
+                  buf.m.offset);
             return nullptr;
         }
 
-        readLocations[i] = static_cast<const int16_t*>(mmap(nullptr /* start anywhere */,
-                buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */,
-                fd.get(), buf.m.offset));
+        readLocations[i] = static_cast<const int16_t*>(
+                mmap(nullptr /* start anywhere */, buf.length, PROT_READ /* required */,
+                     MAP_SHARED /* recommended */, fd.get(), buf.m.offset));
         if (readLocations[i] == MAP_FAILED) {
             ALOGE("%s: map failed: %s", __func__, strerror(errno));
             return nullptr;
@@ -150,8 +153,9 @@
         }
     }
     // Using 'new' to access a non-public constructor.
-    return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(
-            fd.release(), std::move(name), std::move(devicePath), height, width, readLocations));
+    return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(fd.release(), std::move(name),
+                                                                  std::move(devicePath), height,
+                                                                  width, readLocations));
 }
 
 size_t TouchVideoDevice::readAndQueueFrames() {
@@ -163,10 +167,10 @@
     }
     // Concatenate the vectors, then clip up to maximum size allowed
     mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
-            std::make_move_iterator(frames.end()));
+                   std::make_move_iterator(frames.end()));
     if (mFrames.size() > MAX_QUEUE_SIZE) {
         ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
-                mFrames.size() - MAX_QUEUE_SIZE);
+              mFrames.size() - MAX_QUEUE_SIZE);
         mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
     }
     return numFrames;
@@ -193,8 +197,8 @@
     if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
         // We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
         // we can't compare timestamps. Just log a warning, since this is a driver issue
-        ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC",
-                buf.timestamp.tv_sec, buf.timestamp.tv_usec);
+        ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec,
+              buf.timestamp.tv_usec);
     }
     std::vector<int16_t> data(mHeight * mWidth);
     const int16_t* readFrom = mReadLocations[buf.index];
@@ -233,7 +237,7 @@
     }
     for (const int16_t* buffer : mReadLocations) {
         void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer));
-        result = munmap(bufferAddress,  mHeight * mWidth * sizeof(int16_t));
+        result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t));
         if (result == -1) {
             ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno));
         }
@@ -242,9 +246,9 @@
 
 std::string TouchVideoDevice::dump() const {
     return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32
-            ", fd=%i, hasValidFd=%s",
-            mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(),
-            hasValidFd() ? "true" : "false");
+                        ", fd=%i, hasValidFd=%s",
+                        mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(),
+                        hasValidFd() ? "true" : "false");
 }
 
 } // namespace android
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/reader/include/EventHub.h
similarity index 79%
rename from services/inputflinger/EventHub.h
rename to services/inputflinger/reader/include/EventHub.h
index 6c3a4a2..c17f3a1 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-//
 #ifndef _RUNTIME_EVENT_HUB_H
 #define _RUNTIME_EVENT_HUB_H
 
@@ -22,17 +21,17 @@
 
 #include <input/Input.h>
 #include <input/InputDevice.h>
-#include <input/Keyboard.h>
-#include <input/KeyLayoutMap.h>
 #include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
-#include <utils/Mutex.h>
-#include <utils/Log.h>
-#include <utils/List.h>
-#include <utils/Errors.h>
-#include <utils/PropertyMap.h>
-#include <utils/KeyedVector.h>
 #include <utils/BitSet.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/PropertyMap.h>
 
 #include <linux/input.h>
 #include <sys/epoll.h>
@@ -41,8 +40,8 @@
 
 /* Convenience constants. */
 
-#define BTN_FIRST 0x100  // first button code
-#define BTN_LAST 0x15f   // last button code
+#define BTN_FIRST 0x100 // first button code
+#define BTN_LAST 0x15f  // last button code
 
 namespace android {
 
@@ -61,10 +60,10 @@
 struct RawAbsoluteAxisInfo {
     bool valid; // true if the information is valid, false otherwise
 
-    int32_t minValue;  // minimum value
-    int32_t maxValue;  // maximum value
-    int32_t flat;      // center flat position, eg. flat == 8 means center is between -8 and 8
-    int32_t fuzz;      // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+    int32_t minValue;   // minimum value
+    int32_t maxValue;   // maximum value
+    int32_t flat;       // center flat position, eg. flat == 8 means center is between -8 and 8
+    int32_t fuzz;       // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
     int32_t resolution; // resolution in units per mm or radians per mm
 
     inline void clear() {
@@ -82,37 +81,37 @@
  */
 enum {
     /* The input device is a keyboard or has buttons. */
-    INPUT_DEVICE_CLASS_KEYBOARD      = 0x00000001,
+    INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
 
     /* The input device is an alpha-numeric keyboard (not just a dial pad). */
-    INPUT_DEVICE_CLASS_ALPHAKEY      = 0x00000002,
+    INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
 
     /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
-    INPUT_DEVICE_CLASS_TOUCH         = 0x00000004,
+    INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
 
     /* The input device is a cursor device such as a trackball or mouse. */
-    INPUT_DEVICE_CLASS_CURSOR        = 0x00000008,
+    INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
 
     /* The input device is a multi-touch touchscreen. */
-    INPUT_DEVICE_CLASS_TOUCH_MT      = 0x00000010,
+    INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
 
     /* The input device is a directional pad (implies keyboard, has DPAD keys). */
-    INPUT_DEVICE_CLASS_DPAD          = 0x00000020,
+    INPUT_DEVICE_CLASS_DPAD = 0x00000020,
 
     /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
-    INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,
+    INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
 
     /* The input device has switches. */
-    INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,
+    INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
 
     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
-    INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,
+    INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
 
     /* The input device has a vibrator (supports FF_RUMBLE). */
-    INPUT_DEVICE_CLASS_VIBRATOR      = 0x00000200,
+    INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
 
     /* The input device has a microphone. */
-    INPUT_DEVICE_CLASS_MIC           = 0x00000400,
+    INPUT_DEVICE_CLASS_MIC = 0x00000400,
 
     /* The input device is an external stylus (has data we want to fuse with touch data). */
     INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800,
@@ -121,10 +120,10 @@
     INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000,
 
     /* The input device is virtual (not a real device, not part of UI configuration). */
-    INPUT_DEVICE_CLASS_VIRTUAL       = 0x40000000,
+    INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
 
     /* The input device is external (not built-in). */
-    INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,
+    INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
 };
 
 /*
@@ -148,8 +147,8 @@
  */
 class EventHubInterface {
 public:
-    EventHubInterface() { }
-    virtual ~EventHubInterface() { }
+    EventHubInterface() {}
+    virtual ~EventHubInterface() {}
 
     // Synthetic raw event type codes produced when devices are added or removed.
     enum {
@@ -173,18 +172,17 @@
     virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
 
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-            RawAbsoluteAxisInfo* outAxisInfo) const = 0;
+                                         RawAbsoluteAxisInfo* outAxisInfo) const = 0;
 
     virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
 
     virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
 
-    virtual status_t mapKey(int32_t deviceId,
-            int32_t scanCode, int32_t usageCode, int32_t metaState,
-            int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const = 0;
+    virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+                            int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+                            uint32_t* outFlags) const = 0;
 
-    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
-            AxisInfo* outAxisInfo) const = 0;
+    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const = 0;
 
     // Sets devices that are excluded from opening.
     // This can be used to ignore input devices for sensors.
@@ -212,13 +210,13 @@
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
     virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
-            int32_t* outValue) const = 0;
+                                          int32_t* outValue) const = 0;
 
     /*
      * Examine key input devices for specific framework keycode support
      */
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-            uint8_t* outFlags) const = 0;
+                                       uint8_t* outFlags) const = 0;
 
     virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
 
@@ -226,8 +224,8 @@
     virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
     virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-            std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
+    virtual void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
 
     virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
     virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
@@ -258,67 +256,68 @@
     virtual status_t disableDevice(int32_t deviceId) = 0;
 };
 
-class EventHub : public EventHubInterface
-{
+class EventHub : public EventHubInterface {
 public:
     EventHub();
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const;
+    virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
+    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
 
-    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const;
+    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
 
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-            RawAbsoluteAxisInfo* outAxisInfo) const;
+                                         RawAbsoluteAxisInfo* outAxisInfo) const override;
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
+    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
 
-    virtual bool hasInputProperty(int32_t deviceId, int property) const;
+    virtual bool hasInputProperty(int32_t deviceId, int property) const override;
 
-    virtual status_t mapKey(int32_t deviceId,
-            int32_t scanCode, int32_t usageCode, int32_t metaState,
-            int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const;
+    virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+                            int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+                            uint32_t* outFlags) const override;
 
     virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
-            AxisInfo* outAxisInfo) const;
+                             AxisInfo* outAxisInfo) const override;
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices);
+    virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
+    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                          int32_t* outValue) const override;
 
-    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags) const;
+    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+                                       uint8_t* outFlags) const override;
 
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
-    virtual bool hasLed(int32_t deviceId, int32_t led) const;
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+    virtual bool hasLed(int32_t deviceId, int32_t led) const override;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-            std::vector<VirtualKeyDefinition>& outVirtualKeys) const;
+    virtual void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
+    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
+    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                          const sp<KeyCharacterMap>& map) override;
 
-    virtual void vibrate(int32_t deviceId, nsecs_t duration);
-    virtual void cancelVibrate(int32_t deviceId);
+    virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
+    virtual void cancelVibrate(int32_t deviceId) override;
 
-    virtual void requestReopenDevices();
+    virtual void requestReopenDevices() override;
 
-    virtual void wake();
+    virtual void wake() override;
 
-    virtual void dump(std::string& dump);
-    virtual void monitor();
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
 
-    virtual ~EventHub();
+    virtual ~EventHub() override;
 
 private:
     struct Device {
@@ -355,7 +354,7 @@
         int32_t controllerNumber;
 
         Device(int fd, int32_t id, const std::string& path,
-                const InputDeviceIdentifier& identifier);
+               const InputDeviceIdentifier& identifier);
         ~Device();
 
         void close();
@@ -380,16 +379,16 @@
     void addDeviceLocked(Device* device);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier);
 
-    void closeDeviceByPathLocked(const char *devicePath);
+    void closeDeviceByPathLocked(const char* devicePath);
     void closeVideoDeviceByPathLocked(const std::string& devicePath);
     void closeDeviceLocked(Device* device);
     void closeAllDevicesLocked();
 
     void configureFd(Device* device);
 
-    bool isDeviceEnabled(int32_t deviceId);
-    status_t enableDevice(int32_t deviceId);
-    status_t disableDevice(int32_t deviceId);
+    bool isDeviceEnabled(int32_t deviceId) override;
+    status_t enableDevice(int32_t deviceId) override;
+    status_t disableDevice(int32_t deviceId) override;
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
     status_t registerDeviceForEpollLocked(Device* device);
@@ -397,7 +396,7 @@
     status_t unregisterDeviceFromEpollLocked(Device* device);
     void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
 
-    status_t scanDirLocked(const char *dirname);
+    status_t scanDirLocked(const char* dirname);
     status_t scanVideoDirLocked(const std::string& dirname);
     void scanDevicesLocked();
     status_t readNotifyLocked();
@@ -453,8 +452,8 @@
      */
     std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
 
-    Device *mOpeningDevices;
-    Device *mClosingDevices;
+    Device* mOpeningDevices;
+    Device* mClosingDevices;
 
     bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
new file mode 100644
index 0000000..882407d
--- /dev/null
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_DEVICE_H
+#define _UI_INPUTREADER_INPUT_DEVICE_H
+
+#include "EventHub.h"
+#include "InputReaderBase.h"
+#include "InputReaderContext.h"
+
+#include <input/DisplayViewport.h>
+#include <input/InputDevice.h>
+#include <utils/PropertyMap.h>
+
+#include <stdint.h>
+#include <optional>
+#include <vector>
+
+namespace android {
+
+class InputMapper;
+
+/* Represents the state of a single input device. */
+class InputDevice {
+public:
+    InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+                int32_t controllerNumber, const InputDeviceIdentifier& identifier,
+                uint32_t classes);
+    ~InputDevice();
+
+    inline InputReaderContext* getContext() { return mContext; }
+    inline int32_t getId() const { return mId; }
+    inline int32_t getControllerNumber() const { return mControllerNumber; }
+    inline int32_t getGeneration() const { return mGeneration; }
+    inline const std::string getName() const { return mIdentifier.name; }
+    inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+    inline uint32_t getClasses() const { return mClasses; }
+    inline uint32_t getSources() const { return mSources; }
+
+    inline bool isExternal() { return mIsExternal; }
+    inline void setExternal(bool external) { mIsExternal = external; }
+    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+        return mAssociatedDisplayPort;
+    }
+    inline std::optional<DisplayViewport> getAssociatedViewport() const {
+        return mAssociatedViewport;
+    }
+    inline void setMic(bool hasMic) { mHasMic = hasMic; }
+    inline bool hasMic() const { return mHasMic; }
+
+    inline bool isIgnored() { return mMappers.empty(); }
+
+    bool isEnabled();
+    void setEnabled(bool enabled, nsecs_t when);
+
+    void dump(std::string& dump);
+    void addMapper(InputMapper* mapper);
+    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
+    void reset(nsecs_t when);
+    void process(const RawEvent* rawEvents, size_t count);
+    void timeoutExpired(nsecs_t when);
+    void updateExternalStylusState(const StylusState& state);
+
+    void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags);
+    void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+    void cancelVibrate(int32_t token);
+    void cancelTouch(nsecs_t when);
+
+    int32_t getMetaState();
+    void updateMetaState(int32_t keyCode);
+
+    void fadePointer();
+
+    void bumpGeneration();
+
+    void notifyReset(nsecs_t when);
+
+    inline const PropertyMap& getConfiguration() { return mConfiguration; }
+    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+    bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); }
+
+    bool hasAbsoluteAxis(int32_t code) {
+        RawAbsoluteAxisInfo info;
+        getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
+        return info.valid;
+    }
+
+    bool isKeyPressed(int32_t code) {
+        return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+    }
+
+    int32_t getAbsoluteAxisValue(int32_t code) {
+        int32_t value;
+        getEventHub()->getAbsoluteAxisValue(mId, code, &value);
+        return value;
+    }
+
+    std::optional<int32_t> getAssociatedDisplayId();
+
+private:
+    InputReaderContext* mContext;
+    int32_t mId;
+    int32_t mGeneration;
+    int32_t mControllerNumber;
+    InputDeviceIdentifier mIdentifier;
+    std::string mAlias;
+    uint32_t mClasses;
+
+    std::vector<InputMapper*> mMappers;
+
+    uint32_t mSources;
+    bool mIsExternal;
+    std::optional<uint8_t> mAssociatedDisplayPort;
+    std::optional<DisplayViewport> mAssociatedViewport;
+    bool mHasMic;
+    bool mDropUntilNextSync;
+
+    typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+    int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
+
+    PropertyMap mConfiguration;
+};
+
+} // namespace android
+
+#endif //_UI_INPUTREADER_INPUT_DEVICE_H
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
new file mode 100644
index 0000000..7b4321e
--- /dev/null
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_READER_H
+#define _UI_INPUTREADER_INPUT_READER_H
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderBase.h"
+#include "InputReaderContext.h"
+
+#include <utils/Condition.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+
+#include <vector>
+
+namespace android {
+
+class InputDevice;
+class InputMapper;
+struct StylusState;
+
+/* The input reader reads raw event data from the event hub and processes it into input events
+ * that it sends to the input listener.  Some functions of the input reader, such as early
+ * event filtering in low power states, are controlled by a separate policy object.
+ *
+ * The InputReader owns a collection of InputMappers.  Most of the work it does happens
+ * on the input reader thread but the InputReader can receive queries from other system
+ * components running on arbitrary threads.  To keep things manageable, the InputReader
+ * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
+ * EventHub or the InputReaderPolicy but it is never held while calling into the
+ * InputListener.
+ */
+class InputReader : public InputReaderInterface {
+public:
+    InputReader(std::shared_ptr<EventHubInterface> eventHub,
+                const sp<InputReaderPolicyInterface>& policy,
+                const sp<InputListenerInterface>& listener);
+    virtual ~InputReader();
+
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
+
+    virtual void loopOnce() override;
+
+    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+
+    virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+                                     int32_t scanCode) override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+                                    int32_t keyCode) override;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+
+    virtual void toggleCapsLockState(int32_t deviceId) override;
+
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+                         const int32_t* keyCodes, uint8_t* outFlags) override;
+
+    virtual void requestRefreshConfiguration(uint32_t changes) override;
+
+    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+                         ssize_t repeat, int32_t token) override;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+
+protected:
+    // These members are protected so they can be instrumented by test cases.
+    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
+                                            const InputDeviceIdentifier& identifier,
+                                            uint32_t classes);
+
+    class ContextImpl : public InputReaderContext {
+        InputReader* mReader;
+
+    public:
+        explicit ContextImpl(InputReader* reader);
+
+        virtual void updateGlobalMetaState() override;
+        virtual int32_t getGlobalMetaState() override;
+        virtual void disableVirtualKeysUntil(nsecs_t time) override;
+        virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
+                                          int32_t scanCode) override;
+        virtual void fadePointer() override;
+        virtual void requestTimeoutAtTime(nsecs_t when) override;
+        virtual int32_t bumpGeneration() override;
+        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+        virtual void dispatchExternalStylusState(const StylusState& outState) override;
+        virtual InputReaderPolicyInterface* getPolicy() override;
+        virtual InputListenerInterface* getListener() override;
+        virtual EventHubInterface* getEventHub() override;
+        virtual uint32_t getNextSequenceNum() override;
+    } mContext;
+
+    friend class ContextImpl;
+
+private:
+    Mutex mLock;
+
+    Condition mReaderIsAliveCondition;
+
+    // This could be unique_ptr, but due to the way InputReader tests are written,
+    // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
+    // in parallel to passing it to the InputReader.
+    std::shared_ptr<EventHubInterface> mEventHub;
+    sp<InputReaderPolicyInterface> mPolicy;
+    sp<QueuedInputListener> mQueuedListener;
+
+    InputReaderConfiguration mConfig;
+
+    // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
+    uint32_t mNextSequenceNum;
+
+    // The event queue.
+    static const int EVENT_BUFFER_SIZE = 256;
+    RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
+
+    KeyedVector<int32_t, InputDevice*> mDevices;
+
+    // low-level input event decoding and device management
+    void processEventsLocked(const RawEvent* rawEvents, size_t count);
+
+    void addDeviceLocked(nsecs_t when, int32_t deviceId);
+    void removeDeviceLocked(nsecs_t when, int32_t deviceId);
+    void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+    void timeoutExpiredLocked(nsecs_t when);
+
+    void handleConfigurationChangedLocked(nsecs_t when);
+
+    int32_t mGlobalMetaState;
+    void updateGlobalMetaStateLocked();
+    int32_t getGlobalMetaStateLocked();
+
+    void notifyExternalStylusPresenceChanged();
+    void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
+    void dispatchExternalStylusState(const StylusState& state);
+
+    void fadePointerLocked();
+
+    int32_t mGeneration;
+    int32_t bumpGenerationLocked();
+
+    void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
+
+    nsecs_t mDisableVirtualKeysTimeout;
+    void disableVirtualKeysUntilLocked(nsecs_t time);
+    bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
+                                    int32_t scanCode);
+
+    nsecs_t mNextTimeout;
+    void requestTimeoutAtTimeLocked(nsecs_t when);
+
+    uint32_t mConfigurationChangesToRefresh;
+    void refreshConfigurationLocked(uint32_t changes);
+
+    // state queries
+    typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+    int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
+                           GetStateFunc getStateFunc);
+    bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+                                     const int32_t* keyCodes, uint8_t* outFlags);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_READER_H
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
new file mode 100644
index 0000000..3472346
--- /dev/null
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+
+#include <input/InputDevice.h>
+
+#include <vector>
+
+namespace android {
+
+class EventHubInterface;
+class InputDevice;
+class InputListenerInterface;
+class InputMapper;
+class InputReaderPolicyInterface;
+struct StylusState;
+
+/* Internal interface used by individual input devices to access global input device state
+ * and parameters maintained by the input reader.
+ */
+class InputReaderContext {
+public:
+    InputReaderContext() {}
+    virtual ~InputReaderContext() {}
+
+    virtual void updateGlobalMetaState() = 0;
+    virtual int32_t getGlobalMetaState() = 0;
+
+    virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
+    virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
+                                      int32_t scanCode) = 0;
+
+    virtual void fadePointer() = 0;
+
+    virtual void requestTimeoutAtTime(nsecs_t when) = 0;
+    virtual int32_t bumpGeneration() = 0;
+
+    virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
+    virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
+
+    virtual InputReaderPolicyInterface* getPolicy() = 0;
+    virtual InputListenerInterface* getListener() = 0;
+    virtual EventHubInterface* getEventHub() = 0;
+
+    virtual uint32_t getNextSequenceNum() = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
new file mode 100644
index 0000000..17f158c
--- /dev/null
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_STYLUS_STATE_H
+#define _UI_INPUTREADER_STYLUS_STATE_H
+
+#include <input/Input.h>
+
+#include <limits.h>
+
+namespace android {
+
+struct StylusState {
+    /* Time the stylus event was received. */
+    nsecs_t when;
+    /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
+    float pressure;
+    /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
+    uint32_t buttons;
+    /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
+    int32_t toolType;
+
+    void copyFrom(const StylusState& other) {
+        when = other.when;
+        pressure = other.pressure;
+        buttons = other.buttons;
+        toolType = other.toolType;
+    }
+
+    void clear() {
+        when = LLONG_MAX;
+        pressure = 0.f;
+        buttons = 0;
+        toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+    }
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_STYLUS_STATE_H
diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
similarity index 92%
rename from services/inputflinger/TouchVideoDevice.h
rename to services/inputflinger/reader/include/TouchVideoDevice.h
index 0e7e2ef..5a32443 100644
--- a/services/inputflinger/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
-#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
 
-#include <array>
 #include <android-base/unique_fd.h>
 #include <input/TouchVideoFrame.h>
-#include <optional>
 #include <stdint.h>
+#include <array>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -109,9 +109,9 @@
      * The constructor is private because opening a v4l2 device requires many checks.
      * To get a new TouchVideoDevice, use 'create' instead.
      */
-    explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
-            uint32_t height, uint32_t width,
-            const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
+    explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, uint32_t height,
+                              uint32_t width,
+                              const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
     /**
      * Read all currently available frames.
      */
@@ -121,5 +121,7 @@
      */
     std::optional<TouchVideoFrame> readFrame();
 };
+
 } // namespace android
-#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+
+#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
new file mode 100644
index 0000000..f69138e
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "CursorInputMapper.h"
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "TouchCursorInputMapperCommon.h"
+
+namespace android {
+
+// --- CursorMotionAccumulator ---
+
+CursorMotionAccumulator::CursorMotionAccumulator() {
+    clearRelativeAxes();
+}
+
+void CursorMotionAccumulator::reset(InputDevice* device) {
+    clearRelativeAxes();
+}
+
+void CursorMotionAccumulator::clearRelativeAxes() {
+    mRelX = 0;
+    mRelY = 0;
+}
+
+void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_REL) {
+        switch (rawEvent->code) {
+            case REL_X:
+                mRelX = rawEvent->value;
+                break;
+            case REL_Y:
+                mRelY = rawEvent->value;
+                break;
+        }
+    }
+}
+
+void CursorMotionAccumulator::finishSync() {
+    clearRelativeAxes();
+}
+
+// --- CursorInputMapper ---
+
+CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {}
+
+CursorInputMapper::~CursorInputMapper() {}
+
+uint32_t CursorInputMapper::getSources() {
+    return mSource;
+}
+
+void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    if (mParameters.mode == Parameters::MODE_POINTER) {
+        float minX, minY, maxX, maxY;
+        if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+            info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
+        }
+    } else {
+        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+    }
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+
+    if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+    }
+    if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+    }
+}
+
+void CursorInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Cursor Input Mapper:\n";
+    dumpParameters(dump);
+    dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
+    dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
+    dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+    dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+    dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
+                         toString(mCursorScrollAccumulator.haveRelativeVWheel()));
+    dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
+                         toString(mCursorScrollAccumulator.haveRelativeHWheel()));
+    dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
+    dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+    dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
+    dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
+    dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
+    dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
+}
+
+void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                  uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+
+    if (!changes) { // first time only
+        mCursorScrollAccumulator.configure(getDevice());
+
+        // Configure basic parameters.
+        configureParameters();
+
+        // Configure device mode.
+        switch (mParameters.mode) {
+            case Parameters::MODE_POINTER_RELATIVE:
+                // Should not happen during first time configuration.
+                ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
+                mParameters.mode = Parameters::MODE_POINTER;
+                [[fallthrough]];
+            case Parameters::MODE_POINTER:
+                mSource = AINPUT_SOURCE_MOUSE;
+                mXPrecision = 1.0f;
+                mYPrecision = 1.0f;
+                mXScale = 1.0f;
+                mYScale = 1.0f;
+                mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+                break;
+            case Parameters::MODE_NAVIGATION:
+                mSource = AINPUT_SOURCE_TRACKBALL;
+                mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+                mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+                mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+                mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+                break;
+        }
+
+        mVWheelScale = 1.0f;
+        mHWheelScale = 1.0f;
+    }
+
+    if ((!changes && config->pointerCapture) ||
+        (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
+        if (config->pointerCapture) {
+            if (mParameters.mode == Parameters::MODE_POINTER) {
+                mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+                mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
+                // Keep PointerController around in order to preserve the pointer position.
+                mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            } else {
+                ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
+            }
+        } else {
+            if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
+                mParameters.mode = Parameters::MODE_POINTER;
+                mSource = AINPUT_SOURCE_MOUSE;
+            } else {
+                ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
+            }
+        }
+        bumpGeneration();
+        if (changes) {
+            getDevice()->notifyReset(when);
+        }
+    }
+
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+        mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
+        mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
+        mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
+    }
+
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        mOrientation = DISPLAY_ORIENTATION_0;
+        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
+            std::optional<DisplayViewport> internalViewport =
+                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+            if (internalViewport) {
+                mOrientation = internalViewport->orientation;
+            }
+        }
+
+        // Update the PointerController if viewports changed.
+        if (mParameters.mode == Parameters::MODE_POINTER) {
+            getPolicy()->obtainPointerController(getDeviceId());
+        }
+        bumpGeneration();
+    }
+}
+
+void CursorInputMapper::configureParameters() {
+    mParameters.mode = Parameters::MODE_POINTER;
+    String8 cursorModeString;
+    if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
+        if (cursorModeString == "navigation") {
+            mParameters.mode = Parameters::MODE_NAVIGATION;
+        } else if (cursorModeString != "pointer" && cursorModeString != "default") {
+            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
+        }
+    }
+
+    mParameters.orientationAware = false;
+    getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+                                                   mParameters.orientationAware);
+
+    mParameters.hasAssociatedDisplay = false;
+    if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+        mParameters.hasAssociatedDisplay = true;
+    }
+}
+
+void CursorInputMapper::dumpParameters(std::string& dump) {
+    dump += INDENT3 "Parameters:\n";
+    dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
+                         toString(mParameters.hasAssociatedDisplay));
+
+    switch (mParameters.mode) {
+        case Parameters::MODE_POINTER:
+            dump += INDENT4 "Mode: pointer\n";
+            break;
+        case Parameters::MODE_POINTER_RELATIVE:
+            dump += INDENT4 "Mode: relative pointer\n";
+            break;
+        case Parameters::MODE_NAVIGATION:
+            dump += INDENT4 "Mode: navigation\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+}
+
+void CursorInputMapper::reset(nsecs_t when) {
+    mButtonState = 0;
+    mDownTime = 0;
+
+    mPointerVelocityControl.reset();
+    mWheelXVelocityControl.reset();
+    mWheelYVelocityControl.reset();
+
+    mCursorButtonAccumulator.reset(getDevice());
+    mCursorMotionAccumulator.reset(getDevice());
+    mCursorScrollAccumulator.reset(getDevice());
+
+    InputMapper::reset(when);
+}
+
+void CursorInputMapper::process(const RawEvent* rawEvent) {
+    mCursorButtonAccumulator.process(rawEvent);
+    mCursorMotionAccumulator.process(rawEvent);
+    mCursorScrollAccumulator.process(rawEvent);
+
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+}
+
+void CursorInputMapper::sync(nsecs_t when) {
+    int32_t lastButtonState = mButtonState;
+    int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
+    mButtonState = currentButtonState;
+
+    bool wasDown = isPointerDown(lastButtonState);
+    bool down = isPointerDown(currentButtonState);
+    bool downChanged;
+    if (!wasDown && down) {
+        mDownTime = when;
+        downChanged = true;
+    } else if (wasDown && !down) {
+        downChanged = true;
+    } else {
+        downChanged = false;
+    }
+    nsecs_t downTime = mDownTime;
+    bool buttonsChanged = currentButtonState != lastButtonState;
+    int32_t buttonsPressed = currentButtonState & ~lastButtonState;
+    int32_t buttonsReleased = lastButtonState & ~currentButtonState;
+
+    float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
+    float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
+    bool moved = deltaX != 0 || deltaY != 0;
+
+    // Rotate delta according to orientation if needed.
+    if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
+        (deltaX != 0.0f || deltaY != 0.0f)) {
+        rotateDelta(mOrientation, &deltaX, &deltaY);
+    }
+
+    // Move the pointer.
+    PointerProperties pointerProperties;
+    pointerProperties.clear();
+    pointerProperties.id = 0;
+    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
+
+    PointerCoords pointerCoords;
+    pointerCoords.clear();
+
+    float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
+    float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
+    bool scrolled = vscroll != 0 || hscroll != 0;
+
+    mWheelYVelocityControl.move(when, nullptr, &vscroll);
+    mWheelXVelocityControl.move(when, &hscroll, nullptr);
+
+    mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+    int32_t displayId;
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    if (mSource == AINPUT_SOURCE_MOUSE) {
+        if (moved || scrolled || buttonsChanged) {
+            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+
+            if (moved) {
+                mPointerController->move(deltaX, deltaY);
+            }
+
+            if (buttonsChanged) {
+                mPointerController->setButtonState(currentButtonState);
+            }
+
+            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+        }
+
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
+        displayId = mPointerController->getDisplayId();
+    } else {
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+        displayId = ADISPLAY_ID_NONE;
+    }
+
+    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+    // Moving an external trackball or mouse should wake the device.
+    // We don't do this for internal cursor devices to prevent them from waking up
+    // the device in your pocket.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    uint32_t policyFlags = 0;
+    if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
+        policyFlags |= POLICY_FLAG_WAKE;
+    }
+
+    // Synthesize key down from buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
+                         displayId, policyFlags, lastButtonState, currentButtonState);
+
+    // Send motion event.
+    if (downChanged || moved || scrolled || buttonsChanged) {
+        int32_t metaState = mContext->getGlobalMetaState();
+        int32_t buttonState = lastButtonState;
+        int32_t motionEventAction;
+        if (downChanged) {
+            motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+        } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
+            motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+        } else {
+            motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
+        }
+
+        if (buttonsReleased) {
+            BitSet32 released(buttonsReleased);
+            while (!released.isEmpty()) {
+                int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
+                buttonState &= ~actionButton;
+                NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                                             mSource, displayId, policyFlags,
+                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                             metaState, buttonState, MotionClassification::NONE,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                             &pointerCoords, mXPrecision, mYPrecision,
+                                             xCursorPosition, yCursorPosition, downTime,
+                                             /* videoFrames */ {});
+                getListener()->notifyMotion(&releaseArgs);
+            }
+        }
+
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), 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,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+
+        if (buttonsPressed) {
+            BitSet32 pressed(buttonsPressed);
+            while (!pressed.isEmpty()) {
+                int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
+                buttonState |= actionButton;
+                NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                                           mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
+                                           metaState, buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {});
+                getListener()->notifyMotion(&pressArgs);
+            }
+        }
+
+        ALOG_ASSERT(buttonState == currentButtonState);
+
+        // 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(mContext->getNextSequenceNum(), when, 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 */ {});
+            getListener()->notifyMotion(&hoverArgs);
+        }
+
+        // Send scroll events.
+        if (scrolled) {
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+            NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, 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 */ {});
+            getListener()->notifyMotion(&scrollArgs);
+        }
+    }
+
+    // Synthesize key up from buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+                         displayId, policyFlags, lastButtonState, currentButtonState);
+
+    mCursorMotionAccumulator.finishSync();
+    mCursorScrollAccumulator.finishSync();
+}
+
+int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+        return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+    } else {
+        return AKEY_STATE_UNKNOWN;
+    }
+}
+
+void CursorInputMapper::fadePointer() {
+    if (mPointerController != nullptr) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+    }
+}
+
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mParameters.mode == Parameters::MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            // If the device is orientationAware and not a mouse,
+            // it expects to dispatch events to any display
+            return std::make_optional(ADISPLAY_ID_NONE);
+        }
+    }
+    return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
new file mode 100644
index 0000000..77d122a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "InputMapper.h"
+
+#include <PointerControllerInterface.h>
+#include <input/VelocityControl.h>
+
+namespace android {
+
+class VelocityControl;
+class PointerControllerInterface;
+
+class CursorButtonAccumulator;
+class CursorScrollAccumulator;
+
+/* Keeps track of cursor movements. */
+class CursorMotionAccumulator {
+public:
+    CursorMotionAccumulator();
+    void reset(InputDevice* device);
+
+    void process(const RawEvent* rawEvent);
+    void finishSync();
+
+    inline int32_t getRelativeX() const { return mRelX; }
+    inline int32_t getRelativeY() const { return mRelY; }
+
+private:
+    int32_t mRelX;
+    int32_t mRelY;
+
+    void clearRelativeAxes();
+};
+
+class CursorInputMapper : public InputMapper {
+public:
+    explicit CursorInputMapper(InputDevice* device);
+    virtual ~CursorInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+
+    virtual void fadePointer() override;
+
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+private:
+    // Amount that trackball needs to move in order to generate a key event.
+    static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+    // Immutable configuration parameters.
+    struct Parameters {
+        enum Mode {
+            MODE_POINTER,
+            MODE_POINTER_RELATIVE,
+            MODE_NAVIGATION,
+        };
+
+        Mode mode;
+        bool hasAssociatedDisplay;
+        bool orientationAware;
+    } mParameters;
+
+    CursorButtonAccumulator mCursorButtonAccumulator;
+    CursorMotionAccumulator mCursorMotionAccumulator;
+    CursorScrollAccumulator mCursorScrollAccumulator;
+
+    int32_t mSource;
+    float mXScale;
+    float mYScale;
+    float mXPrecision;
+    float mYPrecision;
+
+    float mVWheelScale;
+    float mHWheelScale;
+
+    // Velocity controls for mouse pointer and wheel movements.
+    // The controls for X and Y wheel movements are separate to keep them decoupled.
+    VelocityControl mPointerVelocityControl;
+    VelocityControl mWheelXVelocityControl;
+    VelocityControl mWheelYVelocityControl;
+
+    int32_t mOrientation;
+
+    sp<PointerControllerInterface> mPointerController;
+
+    int32_t mButtonState;
+    nsecs_t mDownTime;
+
+    void configureParameters();
+    void dumpParameters(std::string& dump);
+
+    void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
new file mode 100644
index 0000000..9aa0770
--- /dev/null
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "ExternalStylusInputMapper.h"
+
+#include "SingleTouchMotionAccumulator.h"
+#include "TouchButtonAccumulator.h"
+
+namespace android {
+
+ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {}
+
+uint32_t ExternalStylusInputMapper::getSources() {
+    return AINPUT_SOURCE_STYLUS;
+}
+
+void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
+                         0.0f);
+}
+
+void ExternalStylusInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "External Stylus Input Mapper:\n";
+    dump += INDENT3 "Raw Stylus Axes:\n";
+    dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure");
+    dump += INDENT3 "Stylus State:\n";
+    dumpStylusState(dump, mStylusState);
+}
+
+void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                          uint32_t changes) {
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
+    mTouchButtonAccumulator.configure(getDevice());
+}
+
+void ExternalStylusInputMapper::reset(nsecs_t when) {
+    InputDevice* device = getDevice();
+    mSingleTouchMotionAccumulator.reset(device);
+    mTouchButtonAccumulator.reset(device);
+    InputMapper::reset(when);
+}
+
+void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+    mSingleTouchMotionAccumulator.process(rawEvent);
+    mTouchButtonAccumulator.process(rawEvent);
+
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+}
+
+void ExternalStylusInputMapper::sync(nsecs_t when) {
+    mStylusState.clear();
+
+    mStylusState.when = when;
+
+    mStylusState.toolType = mTouchButtonAccumulator.getToolType();
+    if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+        mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    }
+
+    int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
+    if (mRawPressureAxis.valid) {
+        mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
+    } else if (mTouchButtonAccumulator.isToolActive()) {
+        mStylusState.pressure = 1.0f;
+    } else {
+        mStylusState.pressure = 0.0f;
+    }
+
+    mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
+
+    mContext->dispatchExternalStylusState(mStylusState);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
new file mode 100644
index 0000000..34f339a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+#include "SingleTouchMotionAccumulator.h"
+#include "StylusState.h"
+#include "TouchButtonAccumulator.h"
+
+namespace android {
+
+class ExternalStylusInputMapper : public InputMapper {
+public:
+    explicit ExternalStylusInputMapper(InputDevice* device);
+    virtual ~ExternalStylusInputMapper() = default;
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+private:
+    SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+    RawAbsoluteAxisInfo mRawPressureAxis;
+    TouchButtonAccumulator mTouchButtonAccumulator;
+
+    StylusState mStylusState;
+
+    void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
new file mode 100644
index 0000000..d941528
--- /dev/null
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "InputMapper.h"
+
+#include "InputDevice.h"
+
+namespace android {
+
+InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {}
+
+InputMapper::~InputMapper() {}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    info->addSource(getSources());
+}
+
+void InputMapper::dump(std::string& dump) {}
+
+void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                            uint32_t changes) {}
+
+void InputMapper::reset(nsecs_t when) {}
+
+void InputMapper::timeoutExpired(nsecs_t when) {}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                        const int32_t* keyCodes, uint8_t* outFlags) {
+    return false;
+}
+
+void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                          int32_t token) {}
+
+void InputMapper::cancelVibrate(int32_t token) {}
+
+void InputMapper::cancelTouch(nsecs_t when) {}
+
+int32_t InputMapper::getMetaState() {
+    return 0;
+}
+
+void InputMapper::updateMetaState(int32_t keyCode) {}
+
+void InputMapper::updateExternalStylusState(const StylusState& state) {}
+
+void InputMapper::fadePointer() {}
+
+status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
+    return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
+}
+
+void InputMapper::bumpGeneration() {
+    mDevice->bumpGeneration();
+}
+
+void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+                                          const char* name) {
+    if (axis.valid) {
+        dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name,
+                             axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution);
+    } else {
+        dump += StringPrintf(INDENT4 "%s: unknown range\n", name);
+    }
+}
+
+void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
+    dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
+    dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
+    dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
+    dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
new file mode 100644
index 0000000..a559ef8
--- /dev/null
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_MAPPER_H
+#define _UI_INPUTREADER_INPUT_MAPPER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+#include "StylusState.h"
+
+namespace android {
+
+/* An input mapper transforms raw input events into cooked event data.
+ * A single input device can have multiple associated input mappers in order to interpret
+ * different classes of events.
+ *
+ * InputMapper lifecycle:
+ * - create
+ * - configure with 0 changes
+ * - reset
+ * - process, process, process (may occasionally reconfigure with non-zero changes or reset)
+ * - reset
+ * - destroy
+ */
+class InputMapper {
+public:
+    explicit InputMapper(InputDevice* device);
+    virtual ~InputMapper();
+
+    inline InputDevice* getDevice() { return mDevice; }
+    inline int32_t getDeviceId() { return mDevice->getId(); }
+    inline const std::string getDeviceName() { return mDevice->getName(); }
+    inline InputReaderContext* getContext() { return mContext; }
+    inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
+    inline InputListenerInterface* getListener() { return mContext->getListener(); }
+    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+    virtual uint32_t getSources() = 0;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(std::string& dump);
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
+    virtual void reset(nsecs_t when);
+    virtual void process(const RawEvent* rawEvent) = 0;
+    virtual void timeoutExpired(nsecs_t when);
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                       const int32_t* keyCodes, uint8_t* outFlags);
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+    virtual void cancelVibrate(int32_t token);
+    virtual void cancelTouch(nsecs_t when);
+
+    virtual int32_t getMetaState();
+    virtual void updateMetaState(int32_t keyCode);
+
+    virtual void updateExternalStylusState(const StylusState& state);
+
+    virtual void fadePointer();
+    virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+
+protected:
+    InputDevice* mDevice;
+    InputReaderContext* mContext;
+
+    status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+    void bumpGeneration();
+
+    static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+                                        const char* name);
+    static void dumpStylusState(std::string& dump, const StylusState& state);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
new file mode 100644
index 0000000..50adf73
--- /dev/null
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "JoystickInputMapper.h"
+
+namespace android {
+
+JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {}
+
+JoystickInputMapper::~JoystickInputMapper() {}
+
+uint32_t JoystickInputMapper::getSources() {
+    return AINPUT_SOURCE_JOYSTICK;
+}
+
+void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    for (size_t i = 0; i < mAxes.size(); i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        addMotionRange(axis.axisInfo.axis, axis, info);
+
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            addMotionRange(axis.axisInfo.highAxis, axis, info);
+        }
+    }
+}
+
+void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) {
+    info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz,
+                         axis.resolution);
+    /* In order to ease the transition for developers from using the old axes
+     * to the newer, more semantically correct axes, we'll continue to register
+     * the old axes as duplicates of their corresponding new ones.  */
+    int32_t compatAxis = getCompatAxis(axisId);
+    if (compatAxis >= 0) {
+        info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat,
+                             axis.fuzz, axis.resolution);
+    }
+}
+
+/* A mapping from axes the joystick actually has to the axes that should be
+ * artificially created for compatibility purposes.
+ * Returns -1 if no compatibility axis is needed. */
+int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
+    switch (axis) {
+        case AMOTION_EVENT_AXIS_LTRIGGER:
+            return AMOTION_EVENT_AXIS_BRAKE;
+        case AMOTION_EVENT_AXIS_RTRIGGER:
+            return AMOTION_EVENT_AXIS_GAS;
+    }
+    return -1;
+}
+
+void JoystickInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Joystick Input Mapper:\n";
+
+    dump += INDENT3 "Axes:\n";
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        const char* label = getAxisLabel(axis.axisInfo.axis);
+        if (label) {
+            dump += StringPrintf(INDENT4 "%s", label);
+        } else {
+            dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
+        }
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            label = getAxisLabel(axis.axisInfo.highAxis);
+            if (label) {
+                dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
+            } else {
+                dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis,
+                                     axis.axisInfo.splitValue);
+            }
+        } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
+            dump += " (invert)";
+        }
+
+        dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
+                             axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
+        dump += StringPrintf(INDENT4 "  scale=%0.5f, offset=%0.5f, "
+                                     "highScale=%0.5f, highOffset=%0.5f\n",
+                             axis.scale, axis.offset, axis.highScale, axis.highOffset);
+        dump += StringPrintf(INDENT4 "  rawAxis=%d, rawMin=%d, rawMax=%d, "
+                                     "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
+                             mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+                             axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
+                             axis.rawAxisInfo.resolution);
+    }
+}
+
+void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                    uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+
+    if (!changes) { // first time only
+        // Collect all axes.
+        for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+            if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+                continue; // axis must be claimed by a different device
+            }
+
+            RawAbsoluteAxisInfo rawAxisInfo;
+            getAbsoluteAxisInfo(abs, &rawAxisInfo);
+            if (rawAxisInfo.valid) {
+                // Map axis.
+                AxisInfo axisInfo;
+                bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
+                if (!explicitlyMapped) {
+                    // Axis is not explicitly mapped, will choose a generic axis later.
+                    axisInfo.mode = AxisInfo::MODE_NORMAL;
+                    axisInfo.axis = -1;
+                }
+
+                // Apply flat override.
+                int32_t rawFlat =
+                        axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+                // Calculate scaling factors and limits.
+                Axis axis;
+                if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+                    float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+                    float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
+                                    0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                                    rawAxisInfo.resolution * scale);
+                } else if (isCenteredAxis(axisInfo.axis)) {
+                    float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                    float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
+                                    offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                                    rawAxisInfo.resolution * scale);
+                } else {
+                    float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+                    axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
+                                    0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                                    rawAxisInfo.resolution * scale);
+                }
+
+                // To eliminate noise while the joystick is at rest, filter out small variations
+                // in axis values up front.
+                axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
+
+                mAxes.add(abs, axis);
+            }
+        }
+
+        // If there are too many axes, start dropping them.
+        // Prefer to keep explicitly mapped axes.
+        if (mAxes.size() > PointerCoords::MAX_AXES) {
+            ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
+                  getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
+            pruneAxes(true);
+            pruneAxes(false);
+        }
+
+        // Assign generic axis ids to remaining axes.
+        int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
+        size_t numAxes = mAxes.size();
+        for (size_t i = 0; i < numAxes; i++) {
+            Axis& axis = mAxes.editValueAt(i);
+            if (axis.axisInfo.axis < 0) {
+                while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
+                       haveAxis(nextGenericAxisId)) {
+                    nextGenericAxisId += 1;
+                }
+
+                if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+                    axis.axisInfo.axis = nextGenericAxisId;
+                    nextGenericAxisId += 1;
+                } else {
+                    ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
+                          "have already been assigned to other axes.",
+                          getDeviceName().c_str(), mAxes.keyAt(i));
+                    mAxes.removeItemsAt(i--);
+                    numAxes -= 1;
+                }
+            }
+        }
+    }
+}
+
+bool JoystickInputMapper::haveAxis(int32_t axisId) {
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        if (axis.axisInfo.axis == axisId ||
+            (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
+    size_t i = mAxes.size();
+    while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
+        if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+            continue;
+        }
+        ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
+              getDeviceName().c_str(), mAxes.keyAt(i));
+        mAxes.removeItemsAt(i);
+    }
+}
+
+bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
+    switch (axis) {
+        case AMOTION_EVENT_AXIS_X:
+        case AMOTION_EVENT_AXIS_Y:
+        case AMOTION_EVENT_AXIS_Z:
+        case AMOTION_EVENT_AXIS_RX:
+        case AMOTION_EVENT_AXIS_RY:
+        case AMOTION_EVENT_AXIS_RZ:
+        case AMOTION_EVENT_AXIS_HAT_X:
+        case AMOTION_EVENT_AXIS_HAT_Y:
+        case AMOTION_EVENT_AXIS_ORIENTATION:
+        case AMOTION_EVENT_AXIS_RUDDER:
+        case AMOTION_EVENT_AXIS_WHEEL:
+            return true;
+        default:
+            return false;
+    }
+}
+
+void JoystickInputMapper::reset(nsecs_t when) {
+    // Recenter all axes.
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        Axis& axis = mAxes.editValueAt(i);
+        axis.resetValue();
+    }
+
+    InputMapper::reset(when);
+}
+
+void JoystickInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+        case EV_ABS: {
+            ssize_t index = mAxes.indexOfKey(rawEvent->code);
+            if (index >= 0) {
+                Axis& axis = mAxes.editValueAt(index);
+                float newValue, highNewValue;
+                switch (axis.axisInfo.mode) {
+                    case AxisInfo::MODE_INVERT:
+                        newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale +
+                                axis.offset;
+                        highNewValue = 0.0f;
+                        break;
+                    case AxisInfo::MODE_SPLIT:
+                        if (rawEvent->value < axis.axisInfo.splitValue) {
+                            newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale +
+                                    axis.offset;
+                            highNewValue = 0.0f;
+                        } else if (rawEvent->value > axis.axisInfo.splitValue) {
+                            newValue = 0.0f;
+                            highNewValue =
+                                    (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale +
+                                    axis.highOffset;
+                        } else {
+                            newValue = 0.0f;
+                            highNewValue = 0.0f;
+                        }
+                        break;
+                    default:
+                        newValue = rawEvent->value * axis.scale + axis.offset;
+                        highNewValue = 0.0f;
+                        break;
+                }
+                axis.newValue = newValue;
+                axis.highNewValue = highNewValue;
+            }
+            break;
+        }
+
+        case EV_SYN:
+            switch (rawEvent->code) {
+                case SYN_REPORT:
+                    sync(rawEvent->when, false /*force*/);
+                    break;
+            }
+            break;
+    }
+}
+
+void JoystickInputMapper::sync(nsecs_t when, bool force) {
+    if (!filterAxes(force)) {
+        return;
+    }
+
+    int32_t metaState = mContext->getGlobalMetaState();
+    int32_t buttonState = 0;
+
+    PointerProperties pointerProperties;
+    pointerProperties.clear();
+    pointerProperties.id = 0;
+    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+    PointerCoords pointerCoords;
+    pointerCoords.clear();
+
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        const Axis& axis = mAxes.valueAt(i);
+        setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
+                                      axis.highCurrentValue);
+        }
+    }
+
+    // Moving a joystick axis should not wake the device because joysticks can
+    // be fairly noisy even when not in use.  On the other hand, pushing a gamepad
+    // button will likely wake the device.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    uint32_t policyFlags = 0;
+
+    NotifyMotionArgs args(mContext->getNextSequenceNum(), 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,
+                          &pointerProperties, &pointerCoords, 0, 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+    getListener()->notifyMotion(&args);
+}
+
+void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
+                                                    float value) {
+    pointerCoords->setAxisValue(axis, value);
+    /* In order to ease the transition for developers from using the old axes
+     * to the newer, more semantically correct axes, we'll continue to produce
+     * values for the old axes as mirrors of the value of their corresponding
+     * new axes. */
+    int32_t compatAxis = getCompatAxis(axis);
+    if (compatAxis >= 0) {
+        pointerCoords->setAxisValue(compatAxis, value);
+    }
+}
+
+bool JoystickInputMapper::filterAxes(bool force) {
+    bool atLeastOneSignificantChange = force;
+    size_t numAxes = mAxes.size();
+    for (size_t i = 0; i < numAxes; i++) {
+        Axis& axis = mAxes.editValueAt(i);
+        if (force ||
+            hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
+                                         axis.max)) {
+            axis.currentValue = axis.newValue;
+            atLeastOneSignificantChange = true;
+        }
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            if (force ||
+                hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue,
+                                             axis.min, axis.max)) {
+                axis.highCurrentValue = axis.highNewValue;
+                atLeastOneSignificantChange = true;
+            }
+        }
+    }
+    return atLeastOneSignificantChange;
+}
+
+bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue,
+                                                       float currentValue, float min, float max) {
+    if (newValue != currentValue) {
+        // Filter out small changes in value unless the value is converging on the axis
+        // bounds or center point.  This is intended to reduce the amount of information
+        // sent to applications by particularly noisy joysticks (such as PS3).
+        if (fabs(newValue - currentValue) > filter ||
+            hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) ||
+            hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) ||
+            hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue,
+                                                                   float currentValue,
+                                                                   float thresholdValue) {
+    float newDistance = fabs(newValue - thresholdValue);
+    if (newDistance < filter) {
+        float oldDistance = fabs(currentValue - thresholdValue);
+        if (newDistance < oldDistance) {
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
new file mode 100644
index 0000000..b46d27d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class JoystickInputMapper : public InputMapper {
+public:
+    explicit JoystickInputMapper(InputDevice* device);
+    virtual ~JoystickInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+private:
+    struct Axis {
+        RawAbsoluteAxisInfo rawAxisInfo;
+        AxisInfo axisInfo;
+
+        bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
+
+        float scale;      // scale factor from raw to normalized values
+        float offset;     // offset to add after scaling for normalization
+        float highScale;  // scale factor from raw to normalized values of high split
+        float highOffset; // offset to add after scaling for normalization of high split
+
+        float min;        // normalized inclusive minimum
+        float max;        // normalized inclusive maximum
+        float flat;       // normalized flat region size
+        float fuzz;       // normalized error tolerance
+        float resolution; // normalized resolution in units/mm
+
+        float filter;           // filter out small variations of this size
+        float currentValue;     // current value
+        float newValue;         // most recent value
+        float highCurrentValue; // current value of high split
+        float highNewValue;     // most recent value of high split
+
+        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+                        bool explicitlyMapped, float scale, float offset, float highScale,
+                        float highOffset, float min, float max, float flat, float fuzz,
+                        float resolution) {
+            this->rawAxisInfo = rawAxisInfo;
+            this->axisInfo = axisInfo;
+            this->explicitlyMapped = explicitlyMapped;
+            this->scale = scale;
+            this->offset = offset;
+            this->highScale = highScale;
+            this->highOffset = highOffset;
+            this->min = min;
+            this->max = max;
+            this->flat = flat;
+            this->fuzz = fuzz;
+            this->resolution = resolution;
+            this->filter = 0;
+            resetValue();
+        }
+
+        void resetValue() {
+            this->currentValue = 0;
+            this->newValue = 0;
+            this->highCurrentValue = 0;
+            this->highNewValue = 0;
+        }
+    };
+
+    // Axes indexed by raw ABS_* axis index.
+    KeyedVector<int32_t, Axis> mAxes;
+
+    void sync(nsecs_t when, bool force);
+
+    bool haveAxis(int32_t axisId);
+    void pruneAxes(bool ignoreExplicitlyMappedAxes);
+    bool filterAxes(bool force);
+
+    static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue,
+                                             float min, float max);
+    static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue,
+                                                         float currentValue, float thresholdValue);
+
+    static bool isCenteredAxis(int32_t axis);
+    static int32_t getCompatAxis(int32_t axis);
+
+    static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
+    static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
new file mode 100644
index 0000000..f51d4a0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "KeyboardInputMapper.h"
+
+namespace android {
+
+// --- Static Definitions ---
+
+static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
+                                           const int32_t map[][4], size_t mapSize) {
+    if (orientation != DISPLAY_ORIENTATION_0) {
+        for (size_t i = 0; i < mapSize; i++) {
+            if (value == map[i][0]) {
+                return map[i][orientation];
+            }
+        }
+    }
+    return value;
+}
+
+static const int32_t keyCodeRotationMap[][4] = {
+        // key codes enumerated counter-clockwise with the original (unrotated) key first
+        // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
+        {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
+        {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
+        {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
+        {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
+        {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+         AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
+        {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+         AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
+        {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+         AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
+        {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+         AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
+};
+
+static const size_t keyCodeRotationMapSize =
+        sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
+
+static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
+                             size_t mapSize) {
+    if (orientation == DISPLAY_ORIENTATION_180) {
+        for (size_t i = 0; i < mapSize; i++) {
+            if (value == map[i][0]) {
+                return map[i][1];
+            }
+        }
+    }
+    return value;
+}
+
+// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
+static int32_t stemKeyRotationMap[][2] = {
+        // key codes enumerated with the original (unrotated) key first
+        // no rotation,           180 degree rotation
+        {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
+        {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
+        {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
+        {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
+};
+
+static const size_t stemKeyRotationMapSize =
+        sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
+
+static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+    keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
+    return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
+                                       keyCodeRotationMapSize);
+}
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType)
+      : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {}
+
+KeyboardInputMapper::~KeyboardInputMapper() {}
+
+uint32_t KeyboardInputMapper::getSources() {
+    return mSource;
+}
+
+int32_t KeyboardInputMapper::getOrientation() {
+    if (mViewport) {
+        return mViewport->orientation;
+    }
+    return DISPLAY_ORIENTATION_0;
+}
+
+int32_t KeyboardInputMapper::getDisplayId() {
+    if (mViewport) {
+        return mViewport->displayId;
+    }
+    return ADISPLAY_ID_NONE;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    info->setKeyboardType(mKeyboardType);
+    info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
+}
+
+void KeyboardInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Keyboard Input Mapper:\n";
+    dumpParameters(dump);
+    dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+    dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
+    dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
+    dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
+    dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
+}
+
+std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
+        nsecs_t when, const InputReaderConfiguration* config) {
+    const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+    if (displayPort) {
+        // Find the viewport that contains the same port
+        return mDevice->getAssociatedViewport();
+    }
+
+    // No associated display defined, try to find default display if orientationAware.
+    if (mParameters.orientationAware) {
+        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    }
+
+    return std::nullopt;
+}
+
+void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                    uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+
+    if (!changes) { // first time only
+        // Configure basic parameters.
+        configureParameters();
+    }
+
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        mViewport = findViewport(when, config);
+    }
+}
+
+static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
+    int32_t mapped = 0;
+    if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
+        for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
+            if (stemKeyRotationMap[i][0] == keyCode) {
+                stemKeyRotationMap[i][1] = mapped;
+                return;
+            }
+        }
+    }
+}
+
+void KeyboardInputMapper::configureParameters() {
+    mParameters.orientationAware = false;
+    const PropertyMap& config = getDevice()->getConfiguration();
+    config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
+
+    if (mParameters.orientationAware) {
+        mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
+        mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
+        mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
+        mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
+    }
+
+    mParameters.handlesKeyRepeat = false;
+    config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+}
+
+void KeyboardInputMapper::dumpParameters(std::string& dump) {
+    dump += INDENT3 "Parameters:\n";
+    dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+    dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
+}
+
+void KeyboardInputMapper::reset(nsecs_t when) {
+    mMetaState = AMETA_NONE;
+    mDownTime = 0;
+    mKeyDowns.clear();
+    mCurrentHidUsage = 0;
+
+    resetLedState();
+
+    InputMapper::reset(when);
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+        case EV_KEY: {
+            int32_t scanCode = rawEvent->code;
+            int32_t usageCode = mCurrentHidUsage;
+            mCurrentHidUsage = 0;
+
+            if (isKeyboardOrGamepadKey(scanCode)) {
+                processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
+            }
+            break;
+        }
+        case EV_MSC: {
+            if (rawEvent->code == MSC_SCAN) {
+                mCurrentHidUsage = rawEvent->value;
+            }
+            break;
+        }
+        case EV_SYN: {
+            if (rawEvent->code == SYN_REPORT) {
+                mCurrentHidUsage = 0;
+            }
+        }
+    }
+}
+
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+    return scanCode < BTN_MOUSE || scanCode >= KEY_OK ||
+            (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
+            (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
+}
+
+bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
+    switch (keyCode) {
+        case AKEYCODE_MEDIA_PLAY:
+        case AKEYCODE_MEDIA_PAUSE:
+        case AKEYCODE_MEDIA_PLAY_PAUSE:
+        case AKEYCODE_MUTE:
+        case AKEYCODE_HEADSETHOOK:
+        case AKEYCODE_MEDIA_STOP:
+        case AKEYCODE_MEDIA_NEXT:
+        case AKEYCODE_MEDIA_PREVIOUS:
+        case AKEYCODE_MEDIA_REWIND:
+        case AKEYCODE_MEDIA_RECORD:
+        case AKEYCODE_MEDIA_FAST_FORWARD:
+        case AKEYCODE_MEDIA_SKIP_FORWARD:
+        case AKEYCODE_MEDIA_SKIP_BACKWARD:
+        case AKEYCODE_MEDIA_STEP_FORWARD:
+        case AKEYCODE_MEDIA_STEP_BACKWARD:
+        case AKEYCODE_MEDIA_AUDIO_TRACK:
+        case AKEYCODE_VOLUME_UP:
+        case AKEYCODE_VOLUME_DOWN:
+        case AKEYCODE_VOLUME_MUTE:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+            return true;
+    }
+    return false;
+}
+
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
+    int32_t keyCode;
+    int32_t keyMetaState;
+    uint32_t policyFlags;
+
+    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode,
+                              &keyMetaState, &policyFlags)) {
+        keyCode = AKEYCODE_UNKNOWN;
+        keyMetaState = mMetaState;
+        policyFlags = 0;
+    }
+
+    if (down) {
+        // Rotate key codes according to orientation if needed.
+        if (mParameters.orientationAware) {
+            keyCode = rotateKeyCode(keyCode, getOrientation());
+        }
+
+        // Add key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key repeat, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns[keyDownIndex].keyCode;
+        } else {
+            // key down
+            if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
+                mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {
+                return;
+            }
+            if (policyFlags & POLICY_FLAG_GESTURE) {
+                mDevice->cancelTouch(when);
+            }
+
+            KeyDown keyDown;
+            keyDown.keyCode = keyCode;
+            keyDown.scanCode = scanCode;
+            mKeyDowns.push_back(keyDown);
+        }
+
+        mDownTime = when;
+    } else {
+        // Remove key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key up, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns[keyDownIndex].keyCode;
+            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
+        } else {
+            // key was not actually down
+            ALOGI("Dropping key up from device %s because the key was not down.  "
+                  "keyCode=%d, scanCode=%d",
+                  getDeviceName().c_str(), keyCode, scanCode);
+            return;
+        }
+    }
+
+    if (updateMetaStateIfNeeded(keyCode, down)) {
+        // If global meta state changed send it along with the key.
+        // If it has not changed then we'll use what keymap gave us,
+        // since key replacement logic might temporarily reset a few
+        // meta bits for given key.
+        keyMetaState = mMetaState;
+    }
+
+    nsecs_t downTime = mDownTime;
+
+    // Key down on external an keyboard should wake the device.
+    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
+    // For internal keyboards, the key layout file should specify the policy flags for
+    // each wake key individually.
+    // TODO: Use the input device configuration to control this behavior more finely.
+    if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+        policyFlags |= POLICY_FLAG_WAKE;
+    }
+
+    if (mParameters.handlesKeyRepeat) {
+        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+    }
+
+    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, 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);
+}
+
+ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+    size_t n = mKeyDowns.size();
+    for (size_t i = 0; i < n; i++) {
+        if (mKeyDowns[i].scanCode == scanCode) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+}
+
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
+
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                                const int32_t* keyCodes, uint8_t* outFlags) {
+    return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
+
+int32_t KeyboardInputMapper::getMetaState() {
+    return mMetaState;
+}
+
+void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+    updateMetaStateIfNeeded(keyCode, false);
+}
+
+bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
+    int32_t oldMetaState = mMetaState;
+    int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
+    bool metaStateChanged = oldMetaState != newMetaState;
+    if (metaStateChanged) {
+        mMetaState = newMetaState;
+        updateLedState(false);
+
+        getContext()->updateGlobalMetaState();
+    }
+
+    return metaStateChanged;
+}
+
+void KeyboardInputMapper::resetLedState() {
+    initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
+    initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
+    initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK);
+
+    updateLedState(true);
+}
+
+void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
+    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+    ledState.on = false;
+}
+
+void KeyboardInputMapper::updateLedState(bool reset) {
+    updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
+    updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
+    updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
+}
+
+void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led,
+                                                    int32_t modifier, bool reset) {
+    if (ledState.avail) {
+        bool desiredState = (mMetaState & modifier) != 0;
+        if (reset || ledState.on != desiredState) {
+            getEventHub()->setLedState(getDeviceId(), led, desiredState);
+            ledState.on = desiredState;
+        }
+    }
+}
+
+std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+    if (mViewport) {
+        return std::make_optional(mViewport->displayId);
+    }
+    return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
new file mode 100644
index 0000000..de2a377
--- /dev/null
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class KeyboardInputMapper : public InputMapper {
+public:
+    KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
+    virtual ~KeyboardInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
+
+    virtual int32_t getMetaState() override;
+    virtual void updateMetaState(int32_t keyCode) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+private:
+    // The current viewport.
+    std::optional<DisplayViewport> mViewport;
+
+    struct KeyDown {
+        int32_t keyCode;
+        int32_t scanCode;
+    };
+
+    uint32_t mSource;
+    int32_t mKeyboardType;
+
+    std::vector<KeyDown> mKeyDowns; // keys that are down
+    int32_t mMetaState;
+    nsecs_t mDownTime; // time of most recent key down
+
+    int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
+
+    struct LedState {
+        bool avail; // led is available
+        bool on;    // we think the led is currently on
+    };
+    LedState mCapsLockLedState;
+    LedState mNumLockLedState;
+    LedState mScrollLockLedState;
+
+    // Immutable configuration parameters.
+    struct Parameters {
+        bool orientationAware;
+        bool handlesKeyRepeat;
+    } mParameters;
+
+    void configureParameters();
+    void dumpParameters(std::string& dump);
+
+    int32_t getOrientation();
+    int32_t getDisplayId();
+
+    bool isKeyboardOrGamepadKey(int32_t scanCode);
+    bool isMediaKey(int32_t keyCode);
+
+    void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
+
+    bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
+
+    ssize_t findKeyDown(int32_t scanCode);
+
+    void resetLedState();
+    void initializeLedState(LedState& ledState, int32_t led);
+    void updateLedState(bool reset);
+    void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
+    std::optional<DisplayViewport> findViewport(nsecs_t when,
+                                                const InputReaderConfiguration* config);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
new file mode 100644
index 0000000..c567c8b
--- /dev/null
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "MultiTouchInputMapper.h"
+
+namespace android {
+
+// --- Constants ---
+
+// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
+static constexpr size_t MAX_SLOTS = 32;
+
+// --- MultiTouchMotionAccumulator ---
+
+MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
+      : mCurrentSlot(-1),
+        mSlots(nullptr),
+        mSlotCount(0),
+        mUsingSlotsProtocol(false),
+        mHaveStylus(false) {}
+
+MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
+    delete[] mSlots;
+}
+
+void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount,
+                                            bool usingSlotsProtocol) {
+    mSlotCount = slotCount;
+    mUsingSlotsProtocol = usingSlotsProtocol;
+    mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
+
+    delete[] mSlots;
+    mSlots = new Slot[slotCount];
+}
+
+void MultiTouchMotionAccumulator::reset(InputDevice* device) {
+    // Unfortunately there is no way to read the initial contents of the slots.
+    // So when we reset the accumulator, we must assume they are all zeroes.
+    if (mUsingSlotsProtocol) {
+        // Query the driver for the current slot index and use it as the initial slot
+        // before we start reading events from the device.  It is possible that the
+        // current slot index will not be the same as it was when the first event was
+        // written into the evdev buffer, which means the input mapper could start
+        // out of sync with the initial state of the events in the evdev buffer.
+        // In the extremely unlikely case that this happens, the data from
+        // two slots will be confused until the next ABS_MT_SLOT event is received.
+        // This can cause the touch point to "jump", but at least there will be
+        // no stuck touches.
+        int32_t initialSlot;
+        status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT,
+                                                                      &initialSlot);
+        if (status) {
+            ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
+            initialSlot = -1;
+        }
+        clearSlots(initialSlot);
+    } else {
+        clearSlots(-1);
+    }
+}
+
+void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+    if (mSlots) {
+        for (size_t i = 0; i < mSlotCount; i++) {
+            mSlots[i].clear();
+        }
+    }
+    mCurrentSlot = initialSlot;
+}
+
+void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_ABS) {
+        bool newSlot = false;
+        if (mUsingSlotsProtocol) {
+            if (rawEvent->code == ABS_MT_SLOT) {
+                mCurrentSlot = rawEvent->value;
+                newSlot = true;
+            }
+        } else if (mCurrentSlot < 0) {
+            mCurrentSlot = 0;
+        }
+
+        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
+#if DEBUG_POINTERS
+            if (newSlot) {
+                ALOGW("MultiTouch device emitted invalid slot index %d but it "
+                      "should be between 0 and %zd; ignoring this slot.",
+                      mCurrentSlot, mSlotCount - 1);
+            }
+#endif
+        } else {
+            Slot* slot = &mSlots[mCurrentSlot];
+
+            switch (rawEvent->code) {
+                case ABS_MT_POSITION_X:
+                    slot->mInUse = true;
+                    slot->mAbsMTPositionX = rawEvent->value;
+                    break;
+                case ABS_MT_POSITION_Y:
+                    slot->mInUse = true;
+                    slot->mAbsMTPositionY = rawEvent->value;
+                    break;
+                case ABS_MT_TOUCH_MAJOR:
+                    slot->mInUse = true;
+                    slot->mAbsMTTouchMajor = rawEvent->value;
+                    break;
+                case ABS_MT_TOUCH_MINOR:
+                    slot->mInUse = true;
+                    slot->mAbsMTTouchMinor = rawEvent->value;
+                    slot->mHaveAbsMTTouchMinor = true;
+                    break;
+                case ABS_MT_WIDTH_MAJOR:
+                    slot->mInUse = true;
+                    slot->mAbsMTWidthMajor = rawEvent->value;
+                    break;
+                case ABS_MT_WIDTH_MINOR:
+                    slot->mInUse = true;
+                    slot->mAbsMTWidthMinor = rawEvent->value;
+                    slot->mHaveAbsMTWidthMinor = true;
+                    break;
+                case ABS_MT_ORIENTATION:
+                    slot->mInUse = true;
+                    slot->mAbsMTOrientation = rawEvent->value;
+                    break;
+                case ABS_MT_TRACKING_ID:
+                    if (mUsingSlotsProtocol && rawEvent->value < 0) {
+                        // The slot is no longer in use but it retains its previous contents,
+                        // which may be reused for subsequent touches.
+                        slot->mInUse = false;
+                    } else {
+                        slot->mInUse = true;
+                        slot->mAbsMTTrackingId = rawEvent->value;
+                    }
+                    break;
+                case ABS_MT_PRESSURE:
+                    slot->mInUse = true;
+                    slot->mAbsMTPressure = rawEvent->value;
+                    break;
+                case ABS_MT_DISTANCE:
+                    slot->mInUse = true;
+                    slot->mAbsMTDistance = rawEvent->value;
+                    break;
+                case ABS_MT_TOOL_TYPE:
+                    slot->mInUse = true;
+                    slot->mAbsMTToolType = rawEvent->value;
+                    slot->mHaveAbsMTToolType = true;
+                    break;
+            }
+        }
+    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
+        // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+        mCurrentSlot += 1;
+    }
+}
+
+void MultiTouchMotionAccumulator::finishSync() {
+    if (!mUsingSlotsProtocol) {
+        clearSlots(-1);
+    }
+}
+
+bool MultiTouchMotionAccumulator::hasStylus() const {
+    return mHaveStylus;
+}
+
+// --- MultiTouchMotionAccumulator::Slot ---
+
+MultiTouchMotionAccumulator::Slot::Slot() {
+    clear();
+}
+
+void MultiTouchMotionAccumulator::Slot::clear() {
+    mInUse = false;
+    mHaveAbsMTTouchMinor = false;
+    mHaveAbsMTWidthMinor = false;
+    mHaveAbsMTToolType = false;
+    mAbsMTPositionX = 0;
+    mAbsMTPositionY = 0;
+    mAbsMTTouchMajor = 0;
+    mAbsMTTouchMinor = 0;
+    mAbsMTWidthMajor = 0;
+    mAbsMTWidthMinor = 0;
+    mAbsMTOrientation = 0;
+    mAbsMTTrackingId = -1;
+    mAbsMTPressure = 0;
+    mAbsMTDistance = 0;
+    mAbsMTToolType = 0;
+}
+
+int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
+    if (mHaveAbsMTToolType) {
+        switch (mAbsMTToolType) {
+            case MT_TOOL_FINGER:
+                return AMOTION_EVENT_TOOL_TYPE_FINGER;
+            case MT_TOOL_PEN:
+                return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+        }
+    }
+    return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+}
+
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {}
+
+void MultiTouchInputMapper::reset(nsecs_t when) {
+    mMultiTouchMotionAccumulator.reset(getDevice());
+
+    mPointerIdBits.clear();
+
+    TouchInputMapper::reset(when);
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+    TouchInputMapper::process(rawEvent);
+
+    mMultiTouchMotionAccumulator.process(rawEvent);
+}
+
+void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
+    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
+    size_t outCount = 0;
+    BitSet32 newPointerIdBits;
+    mHavePointerIds = true;
+
+    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
+        const MultiTouchMotionAccumulator::Slot* inSlot =
+                mMultiTouchMotionAccumulator.getSlot(inIndex);
+        if (!inSlot->isInUse()) {
+            continue;
+        }
+
+        if (outCount >= MAX_POINTERS) {
+#if DEBUG_POINTERS
+            ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+                  "ignoring the rest.",
+                  getDeviceName().c_str(), MAX_POINTERS);
+#endif
+            break; // too many fingers!
+        }
+
+        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
+        outPointer.x = inSlot->getX();
+        outPointer.y = inSlot->getY();
+        outPointer.pressure = inSlot->getPressure();
+        outPointer.touchMajor = inSlot->getTouchMajor();
+        outPointer.touchMinor = inSlot->getTouchMinor();
+        outPointer.toolMajor = inSlot->getToolMajor();
+        outPointer.toolMinor = inSlot->getToolMinor();
+        outPointer.orientation = inSlot->getOrientation();
+        outPointer.distance = inSlot->getDistance();
+        outPointer.tiltX = 0;
+        outPointer.tiltY = 0;
+
+        outPointer.toolType = inSlot->getToolType();
+        if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+            outPointer.toolType = mTouchButtonAccumulator.getToolType();
+            if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+                outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+            }
+        }
+
+        bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
+                (mTouchButtonAccumulator.isHovering() ||
+                 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
+        outPointer.isHovering = isHovering;
+
+        // Assign pointer id using tracking id if available.
+        if (mHavePointerIds) {
+            int32_t trackingId = inSlot->getTrackingId();
+            int32_t id = -1;
+            if (trackingId >= 0) {
+                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
+                    uint32_t n = idBits.clearFirstMarkedBit();
+                    if (mPointerTrackingIdMap[n] == trackingId) {
+                        id = n;
+                    }
+                }
+
+                if (id < 0 && !mPointerIdBits.isFull()) {
+                    id = mPointerIdBits.markFirstUnmarkedBit();
+                    mPointerTrackingIdMap[id] = trackingId;
+                }
+            }
+            if (id < 0) {
+                mHavePointerIds = false;
+                outState->rawPointerData.clearIdBits();
+                newPointerIdBits.clear();
+            } else {
+                outPointer.id = id;
+                outState->rawPointerData.idToIndex[id] = outCount;
+                outState->rawPointerData.markIdBit(id, isHovering);
+                newPointerIdBits.markBit(id);
+            }
+        }
+        outCount += 1;
+    }
+
+    outState->rawPointerData.pointerCount = outCount;
+    mPointerIdBits = newPointerIdBits;
+
+    mMultiTouchMotionAccumulator.finishSync();
+}
+
+void MultiTouchInputMapper::configureRawPointerAxes() {
+    TouchInputMapper::configureRawPointerAxes();
+
+    getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
+    getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
+    getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
+    getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
+    getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
+
+    if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
+        mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
+        size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
+        if (slotCount > MAX_SLOTS) {
+            ALOGW("MultiTouch Device %s reported %zu slots but the framework "
+                  "only supports a maximum of %zu slots at this time.",
+                  getDeviceName().c_str(), slotCount, MAX_SLOTS);
+            slotCount = MAX_SLOTS;
+        }
+        mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
+    } else {
+        mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS,
+                                               false /*usingSlotsProtocol*/);
+    }
+}
+
+bool MultiTouchInputMapper::hasStylus() const {
+    return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
new file mode 100644
index 0000000..a45c3cb
--- /dev/null
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+
+#include "TouchInputMapper.h"
+
+namespace android {
+
+/* Keeps track of the state of multi-touch protocol. */
+class MultiTouchMotionAccumulator {
+public:
+    class Slot {
+    public:
+        inline bool isInUse() const { return mInUse; }
+        inline int32_t getX() const { return mAbsMTPositionX; }
+        inline int32_t getY() const { return mAbsMTPositionY; }
+        inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
+        inline int32_t getTouchMinor() const {
+            return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor;
+        }
+        inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
+        inline int32_t getToolMinor() const {
+            return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor;
+        }
+        inline int32_t getOrientation() const { return mAbsMTOrientation; }
+        inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
+        inline int32_t getPressure() const { return mAbsMTPressure; }
+        inline int32_t getDistance() const { return mAbsMTDistance; }
+        inline int32_t getToolType() const;
+
+    private:
+        friend class MultiTouchMotionAccumulator;
+
+        bool mInUse;
+        bool mHaveAbsMTTouchMinor;
+        bool mHaveAbsMTWidthMinor;
+        bool mHaveAbsMTToolType;
+
+        int32_t mAbsMTPositionX;
+        int32_t mAbsMTPositionY;
+        int32_t mAbsMTTouchMajor;
+        int32_t mAbsMTTouchMinor;
+        int32_t mAbsMTWidthMajor;
+        int32_t mAbsMTWidthMinor;
+        int32_t mAbsMTOrientation;
+        int32_t mAbsMTTrackingId;
+        int32_t mAbsMTPressure;
+        int32_t mAbsMTDistance;
+        int32_t mAbsMTToolType;
+
+        Slot();
+        void clear();
+    };
+
+    MultiTouchMotionAccumulator();
+    ~MultiTouchMotionAccumulator();
+
+    void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
+    void reset(InputDevice* device);
+    void process(const RawEvent* rawEvent);
+    void finishSync();
+    bool hasStylus() const;
+
+    inline size_t getSlotCount() const { return mSlotCount; }
+    inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
+
+private:
+    int32_t mCurrentSlot;
+    Slot* mSlots;
+    size_t mSlotCount;
+    bool mUsingSlotsProtocol;
+    bool mHaveStylus;
+
+    void clearSlots(int32_t initialSlot);
+};
+
+class MultiTouchInputMapper : public TouchInputMapper {
+public:
+    explicit MultiTouchInputMapper(InputDevice* device);
+    virtual ~MultiTouchInputMapper();
+
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+protected:
+    virtual void syncTouch(nsecs_t when, RawState* outState);
+    virtual void configureRawPointerAxes();
+    virtual bool hasStylus() const;
+
+private:
+    MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
+
+    // Specifies the pointer id bits that are in use, and their associated tracking id.
+    BitSet32 mPointerIdBits;
+    int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
new file mode 100644
index 0000000..e113cca
--- /dev/null
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "RotaryEncoderInputMapper.h"
+
+#include "CursorScrollAccumulator.h"
+
+namespace android {
+
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device)
+      : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
+    mSource = AINPUT_SOURCE_ROTARY_ENCODER;
+}
+
+RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
+
+uint32_t RotaryEncoderInputMapper::getSources() {
+    return mSource;
+}
+
+void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
+        float res = 0.0f;
+        if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
+            ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
+        }
+        if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+                                                        mScalingFactor)) {
+            ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
+                  "default to 1.0!\n");
+            mScalingFactor = 1.0f;
+        }
+        info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                             res * mScalingFactor);
+    }
+}
+
+void RotaryEncoderInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Rotary Encoder Input Mapper:\n";
+    dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
+                         toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
+}
+
+void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                         uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+    if (!changes) {
+        mRotaryEncoderScrollAccumulator.configure(getDevice());
+    }
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        std::optional<DisplayViewport> internalViewport =
+                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+        if (internalViewport) {
+            mOrientation = internalViewport->orientation;
+        } else {
+            mOrientation = DISPLAY_ORIENTATION_0;
+        }
+    }
+}
+
+void RotaryEncoderInputMapper::reset(nsecs_t when) {
+    mRotaryEncoderScrollAccumulator.reset(getDevice());
+
+    InputMapper::reset(when);
+}
+
+void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+    mRotaryEncoderScrollAccumulator.process(rawEvent);
+
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+}
+
+void RotaryEncoderInputMapper::sync(nsecs_t when) {
+    PointerCoords pointerCoords;
+    pointerCoords.clear();
+
+    PointerProperties pointerProperties;
+    pointerProperties.clear();
+    pointerProperties.id = 0;
+    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+    float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+    bool scrolled = scroll != 0;
+
+    // This is not a pointer, so it's not associated with a display.
+    int32_t displayId = ADISPLAY_ID_NONE;
+
+    // Moving the rotary encoder should wake the device (if specified).
+    uint32_t policyFlags = 0;
+    if (scrolled && getDevice()->isExternal()) {
+        policyFlags |= POLICY_FLAG_WAKE;
+    }
+
+    if (mOrientation == DISPLAY_ORIENTATION_180) {
+        scroll = -scroll;
+    }
+
+    // Send motion event.
+    if (scrolled) {
+        int32_t metaState = mContext->getGlobalMetaState();
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
+
+        NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, 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 */ {});
+        getListener()->notifyMotion(&scrollArgs);
+    }
+
+    mRotaryEncoderScrollAccumulator.finishSync();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
new file mode 100644
index 0000000..38c7258
--- /dev/null
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+
+#include "CursorScrollAccumulator.h"
+#include "InputMapper.h"
+
+namespace android {
+
+class RotaryEncoderInputMapper : public InputMapper {
+public:
+    explicit RotaryEncoderInputMapper(InputDevice* device);
+    virtual ~RotaryEncoderInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+private:
+    CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
+
+    int32_t mSource;
+    float mScalingFactor;
+    int32_t mOrientation;
+
+    void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
new file mode 100644
index 0000000..440d282
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SingleTouchInputMapper.h"
+
+namespace android {
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+
+SingleTouchInputMapper::~SingleTouchInputMapper() {}
+
+void SingleTouchInputMapper::reset(nsecs_t when) {
+    mSingleTouchMotionAccumulator.reset(getDevice());
+
+    TouchInputMapper::reset(when);
+}
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+    TouchInputMapper::process(rawEvent);
+
+    mSingleTouchMotionAccumulator.process(rawEvent);
+}
+
+void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
+    if (mTouchButtonAccumulator.isToolActive()) {
+        outState->rawPointerData.pointerCount = 1;
+        outState->rawPointerData.idToIndex[0] = 0;
+
+        bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
+                (mTouchButtonAccumulator.isHovering() ||
+                 (mRawPointerAxes.pressure.valid &&
+                  mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
+        outState->rawPointerData.markIdBit(0, isHovering);
+
+        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0];
+        outPointer.id = 0;
+        outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
+        outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
+        outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
+        outPointer.touchMajor = 0;
+        outPointer.touchMinor = 0;
+        outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
+        outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth();
+        outPointer.orientation = 0;
+        outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance();
+        outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX();
+        outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY();
+        outPointer.toolType = mTouchButtonAccumulator.getToolType();
+        if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+            outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+        }
+        outPointer.isHovering = isHovering;
+    }
+}
+
+void SingleTouchInputMapper::configureRawPointerAxes() {
+    TouchInputMapper::configureRawPointerAxes();
+
+    getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
+    getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
+    getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
+}
+
+bool SingleTouchInputMapper::hasStylus() const {
+    return mTouchButtonAccumulator.hasStylus();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
new file mode 100644
index 0000000..8438eee
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+
+#include "SingleTouchMotionAccumulator.h"
+#include "TouchInputMapper.h"
+
+namespace android {
+
+class SingleTouchInputMapper : public TouchInputMapper {
+public:
+    explicit SingleTouchInputMapper(InputDevice* device);
+    virtual ~SingleTouchInputMapper();
+
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+protected:
+    virtual void syncTouch(nsecs_t when, RawState* outState);
+    virtual void configureRawPointerAxes();
+    virtual bool hasStylus() const;
+
+private:
+    SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
new file mode 100644
index 0000000..4ff941f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "SwitchInputMapper.h"
+
+namespace android {
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device)
+      : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {}
+
+SwitchInputMapper::~SwitchInputMapper() {}
+
+uint32_t SwitchInputMapper::getSources() {
+    return AINPUT_SOURCE_SWITCH;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+        case EV_SW:
+            processSwitch(rawEvent->code, rawEvent->value);
+            break;
+
+        case EV_SYN:
+            if (rawEvent->code == SYN_REPORT) {
+                sync(rawEvent->when);
+            }
+    }
+}
+
+void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
+    if (switchCode >= 0 && switchCode < 32) {
+        if (switchValue) {
+            mSwitchValues |= 1 << switchCode;
+        } else {
+            mSwitchValues &= ~(1 << switchCode);
+        }
+        mUpdatedSwitchMask |= 1 << switchCode;
+    }
+}
+
+void SwitchInputMapper::sync(nsecs_t when) {
+    if (mUpdatedSwitchMask) {
+        uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
+        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
+                              mUpdatedSwitchMask);
+        getListener()->notifySwitch(&args);
+
+        mUpdatedSwitchMask = 0;
+    }
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+    return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+void SwitchInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Switch Input Mapper:\n";
+    dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
new file mode 100644
index 0000000..e65d4e2
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class SwitchInputMapper : public InputMapper {
+public:
+    explicit SwitchInputMapper(InputDevice* device);
+    virtual ~SwitchInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
+    virtual void dump(std::string& dump) override;
+
+private:
+    uint32_t mSwitchValues;
+    uint32_t mUpdatedSwitchMask;
+
+    void processSwitch(int32_t switchCode, int32_t switchValue);
+    void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
new file mode 100644
index 0000000..efa3d6d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+#include <stdint.h>
+
+namespace android {
+
+// --- Static Definitions ---
+
+static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
+    float temp;
+    switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            temp = *deltaX;
+            *deltaX = *deltaY;
+            *deltaY = -temp;
+            break;
+
+        case DISPLAY_ORIENTATION_180:
+            *deltaX = -*deltaX;
+            *deltaY = -*deltaY;
+            break;
+
+        case DISPLAY_ORIENTATION_270:
+            temp = *deltaX;
+            *deltaX = -*deltaY;
+            *deltaY = temp;
+            break;
+    }
+}
+
+// Returns true if the pointer should be reported as being down given the specified
+// button states.  This determines whether the event is reported as a touch event.
+static bool isPointerDown(int32_t buttonState) {
+    return buttonState &
+            (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+             AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+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,
+                                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->getNextSequenceNum(), when, 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,
+                                 int32_t currentButtonState) {
+    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
+                        AKEYCODE_BACK);
+    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
+                        AKEYCODE_FORWARD);
+}
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
new file mode 100644
index 0000000..34603b9
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -0,0 +1,3883 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "TouchInputMapper.h"
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "TouchButtonAccumulator.h"
+#include "TouchCursorInputMapperCommon.h"
+
+namespace android {
+
+// --- Constants ---
+
+// Maximum amount of latency to add to touch events while waiting for data from an
+// external stylus.
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+
+// Maximum amount of time to wait on touch data before pushing out new pressure data.
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+
+// Artificial latency on synthetic events created from stylus data without corresponding touch
+// data.
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// --- Static Definitions ---
+
+template <typename T>
+inline static void swap(T& a, T& b) {
+    T temp = a;
+    a = b;
+    b = temp;
+}
+
+static float calculateCommonVector(float a, float b) {
+    if (a > 0 && b > 0) {
+        return a < b ? a : b;
+    } else if (a < 0 && b < 0) {
+        return a > b ? a : b;
+    } else {
+        return 0;
+    }
+}
+
+inline static float distance(float x1, float y1, float x2, float y2) {
+    return hypotf(x1 - x2, y1 - y2);
+}
+
+inline static int32_t signExtendNybble(int32_t value) {
+    return value >= 8 ? value - 16 : value;
+}
+
+// --- RawPointerAxes ---
+
+RawPointerAxes::RawPointerAxes() {
+    clear();
+}
+
+void RawPointerAxes::clear() {
+    x.clear();
+    y.clear();
+    pressure.clear();
+    touchMajor.clear();
+    touchMinor.clear();
+    toolMajor.clear();
+    toolMinor.clear();
+    orientation.clear();
+    distance.clear();
+    tiltX.clear();
+    tiltY.clear();
+    trackingId.clear();
+    slot.clear();
+}
+
+// --- RawPointerData ---
+
+RawPointerData::RawPointerData() {
+    clear();
+}
+
+void RawPointerData::clear() {
+    pointerCount = 0;
+    clearIdBits();
+}
+
+void RawPointerData::copyFrom(const RawPointerData& other) {
+    pointerCount = other.pointerCount;
+    hoveringIdBits = other.hoveringIdBits;
+    touchingIdBits = other.touchingIdBits;
+
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointers[i] = other.pointers[i];
+
+        int id = pointers[i].id;
+        idToIndex[id] = other.idToIndex[id];
+    }
+}
+
+void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
+    float x = 0, y = 0;
+    uint32_t count = touchingIdBits.count();
+    if (count) {
+        for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const Pointer& pointer = pointerForId(id);
+            x += pointer.x;
+            y += pointer.y;
+        }
+        x /= count;
+        y /= count;
+    }
+    *outX = x;
+    *outY = y;
+}
+
+// --- CookedPointerData ---
+
+CookedPointerData::CookedPointerData() {
+    clear();
+}
+
+void CookedPointerData::clear() {
+    pointerCount = 0;
+    hoveringIdBits.clear();
+    touchingIdBits.clear();
+}
+
+void CookedPointerData::copyFrom(const CookedPointerData& other) {
+    pointerCount = other.pointerCount;
+    hoveringIdBits = other.hoveringIdBits;
+    touchingIdBits = other.touchingIdBits;
+
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+
+        int id = pointerProperties[i].id;
+        idToIndex[id] = other.idToIndex[id];
+    }
+}
+
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device)
+      : InputMapper(device),
+        mSource(0),
+        mDeviceMode(DEVICE_MODE_DISABLED),
+        mSurfaceWidth(-1),
+        mSurfaceHeight(-1),
+        mSurfaceLeft(0),
+        mSurfaceTop(0),
+        mPhysicalWidth(-1),
+        mPhysicalHeight(-1),
+        mPhysicalLeft(0),
+        mPhysicalTop(0),
+        mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}
+
+TouchInputMapper::~TouchInputMapper() {}
+
+uint32_t TouchInputMapper::getSources() {
+    return mSource;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    if (mDeviceMode != DEVICE_MODE_DISABLED) {
+        info->addMotionRange(mOrientedRanges.x);
+        info->addMotionRange(mOrientedRanges.y);
+        info->addMotionRange(mOrientedRanges.pressure);
+
+        if (mOrientedRanges.haveSize) {
+            info->addMotionRange(mOrientedRanges.size);
+        }
+
+        if (mOrientedRanges.haveTouchSize) {
+            info->addMotionRange(mOrientedRanges.touchMajor);
+            info->addMotionRange(mOrientedRanges.touchMinor);
+        }
+
+        if (mOrientedRanges.haveToolSize) {
+            info->addMotionRange(mOrientedRanges.toolMajor);
+            info->addMotionRange(mOrientedRanges.toolMinor);
+        }
+
+        if (mOrientedRanges.haveOrientation) {
+            info->addMotionRange(mOrientedRanges.orientation);
+        }
+
+        if (mOrientedRanges.haveDistance) {
+            info->addMotionRange(mOrientedRanges.distance);
+        }
+
+        if (mOrientedRanges.haveTilt) {
+            info->addMotionRange(mOrientedRanges.tilt);
+        }
+
+        if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+            info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                                 0.0f);
+        }
+        if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+            info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                                 0.0f);
+        }
+        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+            const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+            const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
+                                 x.fuzz, x.resolution);
+            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
+                                 y.fuzz, y.resolution);
+            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
+                                 x.fuzz, x.resolution);
+            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
+                                 y.fuzz, y.resolution);
+        }
+        info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+    }
+}
+
+void TouchInputMapper::dump(std::string& dump) {
+    dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
+    dumpParameters(dump);
+    dumpVirtualKeys(dump);
+    dumpRawPointerAxes(dump);
+    dumpCalibration(dump);
+    dumpAffineTransformation(dump);
+    dumpSurface(dump);
+
+    dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
+    dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
+    dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
+    dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
+    dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
+    dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
+    dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
+    dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
+    dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
+    dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
+    dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
+    dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
+    dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt));
+    dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter);
+    dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale);
+    dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter);
+    dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
+
+    dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
+    dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
+                         mLastRawState.rawPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
+        const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
+        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
+                                     "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
+                                     "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
+                                     "toolType=%d, isHovering=%s\n",
+                             i, pointer.id, pointer.x, pointer.y, pointer.pressure,
+                             pointer.touchMajor, pointer.touchMinor, pointer.toolMajor,
+                             pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY,
+                             pointer.distance, pointer.toolType, toString(pointer.isHovering));
+    }
+
+    dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n",
+                         mLastCookedState.buttonState);
+    dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
+                         mLastCookedState.cookedPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) {
+        const PointerProperties& pointerProperties =
+                mLastCookedState.cookedPointerData.pointerProperties[i];
+        const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
+        dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
+                                     "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, "
+                                     "toolMinor=%0.3f, "
+                                     "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
+                                     "toolType=%d, isHovering=%s\n",
+                             i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT),
+                             pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
+                             pointerProperties.toolType,
+                             toString(mLastCookedState.cookedPointerData.isHovering(i)));
+    }
+
+    dump += INDENT3 "Stylus Fusion:\n";
+    dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
+                         toString(mExternalStylusConnected));
+    dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
+    dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
+                         mExternalStylusFusionTimeout);
+    dump += INDENT3 "External Stylus State:\n";
+    dumpStylusState(dump, mExternalStylusState);
+
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
+        dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale);
+        dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale);
+        dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale);
+        dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale);
+        dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth);
+    }
+}
+
+const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
+    switch (deviceMode) {
+        case DEVICE_MODE_DISABLED:
+            return "disabled";
+        case DEVICE_MODE_DIRECT:
+            return "direct";
+        case DEVICE_MODE_UNSCALED:
+            return "unscaled";
+        case DEVICE_MODE_NAVIGATION:
+            return "navigation";
+        case DEVICE_MODE_POINTER:
+            return "pointer";
+    }
+    return "unknown";
+}
+
+void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                 uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+
+    mConfig = *config;
+
+    if (!changes) { // first time only
+        // Configure basic parameters.
+        configureParameters();
+
+        // Configure common accumulators.
+        mCursorScrollAccumulator.configure(getDevice());
+        mTouchButtonAccumulator.configure(getDevice());
+
+        // Configure absolute axis information.
+        configureRawPointerAxes();
+
+        // Prepare input device calibration.
+        parseCalibration();
+        resolveCalibration();
+    }
+
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
+        // Update location calibration to reflect current settings
+        updateAffineTransformation();
+    }
+
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+        // Update pointer speed.
+        mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
+        mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
+        mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
+    }
+
+    bool resetNeeded = false;
+    if (!changes ||
+        (changes &
+         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+          InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
+          InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
+          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
+        // Configure device sources, surface dimensions, orientation and
+        // scaling factors.
+        configureSurface(when, &resetNeeded);
+    }
+
+    if (changes && resetNeeded) {
+        // Send reset, unless this is the first time the device has been configured,
+        // in which case the reader will call reset itself after all mappers are ready.
+        getDevice()->notifyReset(when);
+    }
+}
+
+void TouchInputMapper::resolveExternalStylusPresence() {
+    std::vector<InputDeviceInfo> devices;
+    mContext->getExternalStylusDevices(devices);
+    mExternalStylusConnected = !devices.empty();
+
+    if (!mExternalStylusConnected) {
+        resetExternalStylus();
+    }
+}
+
+void TouchInputMapper::configureParameters() {
+    // Use the pointer presentation mode for devices that do not support distinct
+    // multitouch.  The spot-based presentation relies on being able to accurately
+    // locate two or more fingers on the touch pad.
+    mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
+            ? Parameters::GESTURE_MODE_SINGLE_TOUCH
+            : Parameters::GESTURE_MODE_MULTI_TOUCH;
+
+    String8 gestureModeString;
+    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+                                                       gestureModeString)) {
+        if (gestureModeString == "single-touch") {
+            mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
+        } else if (gestureModeString == "multi-touch") {
+            mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
+        } else if (gestureModeString != "default") {
+            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
+        }
+    }
+
+    if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
+        // The device is a touch screen.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
+        // The device is a pointing device like a track pad.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+    } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) ||
+               getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
+        // The device is a cursor device with a touch pad attached.
+        // By default don't use the touch pad to move the pointer.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+    } else {
+        // The device is a touch pad of unknown purpose.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+    }
+
+    mParameters.hasButtonUnderPad =
+            getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
+
+    String8 deviceTypeString;
+    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
+                                                       deviceTypeString)) {
+        if (deviceTypeString == "touchScreen") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+        } else if (deviceTypeString == "touchPad") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        } else if (deviceTypeString == "touchNavigation") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
+        } else if (deviceTypeString == "pointer") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+        } else if (deviceTypeString != "default") {
+            ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+        }
+    }
+
+    mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+    getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+                                                   mParameters.orientationAware);
+
+    mParameters.hasAssociatedDisplay = false;
+    mParameters.associatedDisplayIsExternal = false;
+    if (mParameters.orientationAware ||
+        mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN ||
+        mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+        mParameters.hasAssociatedDisplay = true;
+        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+            mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+            String8 uniqueDisplayId;
+            getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
+                                                           uniqueDisplayId);
+            mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
+        }
+    }
+    if (getDevice()->getAssociatedDisplayPort()) {
+        mParameters.hasAssociatedDisplay = true;
+    }
+
+    // Initial downs on external touch devices should wake the device.
+    // Normally we don't do this for internal touch screens to prevent them from waking
+    // up in your pocket but you can enable it using the input device configuration.
+    mParameters.wake = getDevice()->isExternal();
+    getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+}
+
+void TouchInputMapper::dumpParameters(std::string& dump) {
+    dump += INDENT3 "Parameters:\n";
+
+    switch (mParameters.gestureMode) {
+        case Parameters::GESTURE_MODE_SINGLE_TOUCH:
+            dump += INDENT4 "GestureMode: single-touch\n";
+            break;
+        case Parameters::GESTURE_MODE_MULTI_TOUCH:
+            dump += INDENT4 "GestureMode: multi-touch\n";
+            break;
+        default:
+            assert(false);
+    }
+
+    switch (mParameters.deviceType) {
+        case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+            dump += INDENT4 "DeviceType: touchScreen\n";
+            break;
+        case Parameters::DEVICE_TYPE_TOUCH_PAD:
+            dump += INDENT4 "DeviceType: touchPad\n";
+            break;
+        case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+            dump += INDENT4 "DeviceType: touchNavigation\n";
+            break;
+        case Parameters::DEVICE_TYPE_POINTER:
+            dump += INDENT4 "DeviceType: pointer\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
+                                 "displayId='%s'\n",
+                         toString(mParameters.hasAssociatedDisplay),
+                         toString(mParameters.associatedDisplayIsExternal),
+                         mParameters.uniqueDisplayId.c_str());
+    dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+}
+
+void TouchInputMapper::configureRawPointerAxes() {
+    mRawPointerAxes.clear();
+}
+
+void TouchInputMapper::dumpRawPointerAxes(std::string& dump) {
+    dump += INDENT3 "Raw Touch Axes:\n";
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
+}
+
+bool TouchInputMapper::hasExternalStylus() const {
+    return mExternalStylusConnected;
+}
+
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+    if (mParameters.hasAssociatedDisplay) {
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort) {
+            // Find the viewport that contains the same port
+            return mDevice->getAssociatedViewport();
+        }
+
+        // Check if uniqueDisplayId is specified in idc file.
+        if (!mParameters.uniqueDisplayId.empty()) {
+            return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+        }
+
+        ViewportType viewportTypeToUse;
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+
+        std::optional<DisplayViewport> viewport =
+                mConfig.getDisplayViewportByType(viewportTypeToUse);
+        if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+            ALOGW("Input device %s should be associated with external display, "
+                  "fallback to internal one for the external viewport is not found.",
+                  getDeviceName().c_str());
+            viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+        }
+
+        return viewport;
+    }
+
+    // No associated display, return a non-display viewport.
+    DisplayViewport newViewport;
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+    return std::make_optional(newViewport);
+}
+
+void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
+    int32_t oldDeviceMode = mDeviceMode;
+
+    resolveExternalStylusPresence();
+
+    // Determine device mode.
+    if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&
+        mConfig.pointerGesturesEnabled) {
+        mSource = AINPUT_SOURCE_MOUSE;
+        mDeviceMode = DEVICE_MODE_POINTER;
+        if (hasStylus()) {
+            mSource |= AINPUT_SOURCE_STYLUS;
+        }
+    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN &&
+               mParameters.hasAssociatedDisplay) {
+        mSource = AINPUT_SOURCE_TOUCHSCREEN;
+        mDeviceMode = DEVICE_MODE_DIRECT;
+        if (hasStylus()) {
+            mSource |= AINPUT_SOURCE_STYLUS;
+        }
+        if (hasExternalStylus()) {
+            mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
+        }
+    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+        mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+        mDeviceMode = DEVICE_MODE_NAVIGATION;
+    } else {
+        mSource = AINPUT_SOURCE_TOUCHPAD;
+        mDeviceMode = DEVICE_MODE_UNSCALED;
+    }
+
+    // Ensure we have valid X and Y axes.
+    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
+        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
+              "The device will be inoperable.",
+              getDeviceName().c_str());
+        mDeviceMode = DEVICE_MODE_DISABLED;
+        return;
+    }
+
+    // Get associated display dimensions.
+    std::optional<DisplayViewport> newViewport = findViewport();
+    if (!newViewport) {
+        ALOGI("Touch device '%s' could not query the properties of its associated "
+              "display.  The device will be inoperable until the display size "
+              "becomes available.",
+              getDeviceName().c_str());
+        mDeviceMode = DEVICE_MODE_DISABLED;
+        return;
+    }
+
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+
+    bool viewportChanged = mViewport != *newViewport;
+    if (viewportChanged) {
+        mViewport = *newViewport;
+
+        if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+            // Convert rotated viewport to natural surface coordinates.
+            int32_t naturalLogicalWidth, naturalLogicalHeight;
+            int32_t naturalPhysicalWidth, naturalPhysicalHeight;
+            int32_t naturalPhysicalLeft, naturalPhysicalTop;
+            int32_t naturalDeviceWidth, naturalDeviceHeight;
+            switch (mViewport.orientation) {
+                case DISPLAY_ORIENTATION_90:
+                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+                    naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
+                    naturalPhysicalTop = mViewport.physicalLeft;
+                    naturalDeviceWidth = mViewport.deviceHeight;
+                    naturalDeviceHeight = mViewport.deviceWidth;
+                    break;
+                case DISPLAY_ORIENTATION_180:
+                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+                    naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
+                    naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
+                    naturalDeviceWidth = mViewport.deviceWidth;
+                    naturalDeviceHeight = mViewport.deviceHeight;
+                    break;
+                case DISPLAY_ORIENTATION_270:
+                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+                    naturalPhysicalLeft = mViewport.physicalTop;
+                    naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
+                    naturalDeviceWidth = mViewport.deviceHeight;
+                    naturalDeviceHeight = mViewport.deviceWidth;
+                    break;
+                case DISPLAY_ORIENTATION_0:
+                default:
+                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+                    naturalPhysicalLeft = mViewport.physicalLeft;
+                    naturalPhysicalTop = mViewport.physicalTop;
+                    naturalDeviceWidth = mViewport.deviceWidth;
+                    naturalDeviceHeight = mViewport.deviceHeight;
+                    break;
+            }
+
+            if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
+                ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
+                naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
+                naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
+            }
+
+            mPhysicalWidth = naturalPhysicalWidth;
+            mPhysicalHeight = naturalPhysicalHeight;
+            mPhysicalLeft = naturalPhysicalLeft;
+            mPhysicalTop = naturalPhysicalTop;
+
+            mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+            mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+            mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
+            mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+
+            mSurfaceOrientation =
+                    mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
+        } else {
+            mPhysicalWidth = rawWidth;
+            mPhysicalHeight = rawHeight;
+            mPhysicalLeft = 0;
+            mPhysicalTop = 0;
+
+            mSurfaceWidth = rawWidth;
+            mSurfaceHeight = rawHeight;
+            mSurfaceLeft = 0;
+            mSurfaceTop = 0;
+            mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+        }
+    }
+
+    // If moving between pointer modes, need to reset some state.
+    bool deviceModeChanged = mDeviceMode != oldDeviceMode;
+    if (deviceModeChanged) {
+        mOrientedRanges.clear();
+    }
+
+    // Create or update pointer controller if needed.
+    if (mDeviceMode == DEVICE_MODE_POINTER ||
+        (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+        if (mPointerController == nullptr || viewportChanged) {
+            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+        }
+    } else {
+        mPointerController.clear();
+    }
+
+    if (viewportChanged || deviceModeChanged) {
+        ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+              "display id %d",
+              getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
+              mSurfaceOrientation, mDeviceMode, mViewport.displayId);
+
+        // Configure X and Y factors.
+        mXScale = float(mSurfaceWidth) / rawWidth;
+        mYScale = float(mSurfaceHeight) / rawHeight;
+        mXTranslate = -mSurfaceLeft;
+        mYTranslate = -mSurfaceTop;
+        mXPrecision = 1.0f / mXScale;
+        mYPrecision = 1.0f / mYScale;
+
+        mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
+        mOrientedRanges.x.source = mSource;
+        mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
+        mOrientedRanges.y.source = mSource;
+
+        configureVirtualKeys();
+
+        // Scale factor for terms that are not oriented in a particular axis.
+        // If the pixels are square then xScale == yScale otherwise we fake it
+        // by choosing an average.
+        mGeometricScale = avg(mXScale, mYScale);
+
+        // Size of diagonal axis.
+        float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
+
+        // Size factors.
+        if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+            if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
+                mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
+            } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
+                mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
+            } else {
+                mSizeScale = 0.0f;
+            }
+
+            mOrientedRanges.haveTouchSize = true;
+            mOrientedRanges.haveToolSize = true;
+            mOrientedRanges.haveSize = true;
+
+            mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
+            mOrientedRanges.touchMajor.source = mSource;
+            mOrientedRanges.touchMajor.min = 0;
+            mOrientedRanges.touchMajor.max = diagonalSize;
+            mOrientedRanges.touchMajor.flat = 0;
+            mOrientedRanges.touchMajor.fuzz = 0;
+            mOrientedRanges.touchMajor.resolution = 0;
+
+            mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+            mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
+
+            mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
+            mOrientedRanges.toolMajor.source = mSource;
+            mOrientedRanges.toolMajor.min = 0;
+            mOrientedRanges.toolMajor.max = diagonalSize;
+            mOrientedRanges.toolMajor.flat = 0;
+            mOrientedRanges.toolMajor.fuzz = 0;
+            mOrientedRanges.toolMajor.resolution = 0;
+
+            mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
+            mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
+
+            mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
+            mOrientedRanges.size.source = mSource;
+            mOrientedRanges.size.min = 0;
+            mOrientedRanges.size.max = 1.0;
+            mOrientedRanges.size.flat = 0;
+            mOrientedRanges.size.fuzz = 0;
+            mOrientedRanges.size.resolution = 0;
+        } else {
+            mSizeScale = 0.0f;
+        }
+
+        // Pressure factors.
+        mPressureScale = 0;
+        float pressureMax = 1.0;
+        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL ||
+            mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+            if (mCalibration.havePressureScale) {
+                mPressureScale = mCalibration.pressureScale;
+                pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
+            } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
+                mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
+            }
+        }
+
+        mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
+        mOrientedRanges.pressure.source = mSource;
+        mOrientedRanges.pressure.min = 0;
+        mOrientedRanges.pressure.max = pressureMax;
+        mOrientedRanges.pressure.flat = 0;
+        mOrientedRanges.pressure.fuzz = 0;
+        mOrientedRanges.pressure.resolution = 0;
+
+        // Tilt
+        mTiltXCenter = 0;
+        mTiltXScale = 0;
+        mTiltYCenter = 0;
+        mTiltYScale = 0;
+        mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
+        if (mHaveTilt) {
+            mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
+            mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
+            mTiltXScale = M_PI / 180;
+            mTiltYScale = M_PI / 180;
+
+            mOrientedRanges.haveTilt = true;
+
+            mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
+            mOrientedRanges.tilt.source = mSource;
+            mOrientedRanges.tilt.min = 0;
+            mOrientedRanges.tilt.max = M_PI_2;
+            mOrientedRanges.tilt.flat = 0;
+            mOrientedRanges.tilt.fuzz = 0;
+            mOrientedRanges.tilt.resolution = 0;
+        }
+
+        // Orientation
+        mOrientationScale = 0;
+        if (mHaveTilt) {
+            mOrientedRanges.haveOrientation = true;
+
+            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+            mOrientedRanges.orientation.source = mSource;
+            mOrientedRanges.orientation.min = -M_PI;
+            mOrientedRanges.orientation.max = M_PI;
+            mOrientedRanges.orientation.flat = 0;
+            mOrientedRanges.orientation.fuzz = 0;
+            mOrientedRanges.orientation.resolution = 0;
+        } else if (mCalibration.orientationCalibration !=
+                   Calibration::ORIENTATION_CALIBRATION_NONE) {
+            if (mCalibration.orientationCalibration ==
+                Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+                if (mRawPointerAxes.orientation.valid) {
+                    if (mRawPointerAxes.orientation.maxValue > 0) {
+                        mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
+                    } else if (mRawPointerAxes.orientation.minValue < 0) {
+                        mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
+                    } else {
+                        mOrientationScale = 0;
+                    }
+                }
+            }
+
+            mOrientedRanges.haveOrientation = true;
+
+            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+            mOrientedRanges.orientation.source = mSource;
+            mOrientedRanges.orientation.min = -M_PI_2;
+            mOrientedRanges.orientation.max = M_PI_2;
+            mOrientedRanges.orientation.flat = 0;
+            mOrientedRanges.orientation.fuzz = 0;
+            mOrientedRanges.orientation.resolution = 0;
+        }
+
+        // Distance
+        mDistanceScale = 0;
+        if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
+            if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) {
+                if (mCalibration.haveDistanceScale) {
+                    mDistanceScale = mCalibration.distanceScale;
+                } else {
+                    mDistanceScale = 1.0f;
+                }
+            }
+
+            mOrientedRanges.haveDistance = true;
+
+            mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
+            mOrientedRanges.distance.source = mSource;
+            mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale;
+            mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale;
+            mOrientedRanges.distance.flat = 0;
+            mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale;
+            mOrientedRanges.distance.resolution = 0;
+        }
+
+        // Compute oriented precision, scales and ranges.
+        // Note that the maximum value reported is an inclusive maximum value so it is one
+        // unit less than the total width or height of surface.
+        switch (mSurfaceOrientation) {
+            case DISPLAY_ORIENTATION_90:
+            case DISPLAY_ORIENTATION_270:
+                mOrientedXPrecision = mYPrecision;
+                mOrientedYPrecision = mXPrecision;
+
+                mOrientedRanges.x.min = mYTranslate;
+                mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.x.flat = 0;
+                mOrientedRanges.x.fuzz = 0;
+                mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
+
+                mOrientedRanges.y.min = mXTranslate;
+                mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.y.flat = 0;
+                mOrientedRanges.y.fuzz = 0;
+                mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
+                break;
+
+            default:
+                mOrientedXPrecision = mXPrecision;
+                mOrientedYPrecision = mYPrecision;
+
+                mOrientedRanges.x.min = mXTranslate;
+                mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.x.flat = 0;
+                mOrientedRanges.x.fuzz = 0;
+                mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
+
+                mOrientedRanges.y.min = mYTranslate;
+                mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.y.flat = 0;
+                mOrientedRanges.y.fuzz = 0;
+                mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
+                break;
+        }
+
+        // Location
+        updateAffineTransformation();
+
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
+            // Compute pointer gesture detection parameters.
+            float rawDiagonal = hypotf(rawWidth, rawHeight);
+            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
+
+            // Scale movements such that one whole swipe of the touch pad covers a
+            // given area relative to the diagonal size of the display when no acceleration
+            // is applied.
+            // Assume that the touch pad has a square aspect ratio such that movements in
+            // X and Y of the same number of raw units cover the same physical distance.
+            mPointerXMovementScale =
+                    mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal;
+            mPointerYMovementScale = mPointerXMovementScale;
+
+            // Scale zooms to cover a smaller range of the display than movements do.
+            // This value determines the area around the pointer that is affected by freeform
+            // pointer gestures.
+            mPointerXZoomScale =
+                    mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal;
+            mPointerYZoomScale = mPointerXZoomScale;
+
+            // Max width between pointers to detect a swipe gesture is more than some fraction
+            // of the diagonal axis of the touch pad.  Touches that are wider than this are
+            // translated into freeform gestures.
+            mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
+
+            // Abort current pointer usages because the state has changed.
+            abortPointerUsage(when, 0 /*policyFlags*/);
+        }
+
+        // Inform the dispatcher about the changes.
+        *outResetNeeded = true;
+        bumpGeneration();
+    }
+}
+
+void TouchInputMapper::dumpSurface(std::string& dump) {
+    dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
+    dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
+    dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+    dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
+    dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
+    dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
+    dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
+    dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
+    dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
+    dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
+}
+
+void TouchInputMapper::configureVirtualKeys() {
+    std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
+    getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
+
+    mVirtualKeys.clear();
+
+    if (virtualKeyDefinitions.size() == 0) {
+        return;
+    }
+
+    int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
+    int32_t touchScreenTop = mRawPointerAxes.y.minValue;
+    int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+    int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
+
+    for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) {
+        VirtualKey virtualKey;
+
+        virtualKey.scanCode = virtualKeyDefinition.scanCode;
+        int32_t keyCode;
+        int32_t dummyKeyMetaState;
+        uint32_t flags;
+        if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode,
+                                  &dummyKeyMetaState, &flags)) {
+            ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
+            continue; // drop the key
+        }
+
+        virtualKey.keyCode = keyCode;
+        virtualKey.flags = flags;
+
+        // convert the key definition's display coordinates into touch coordinates for a hit box
+        int32_t halfWidth = virtualKeyDefinition.width / 2;
+        int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+        virtualKey.hitLeft =
+                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth +
+                touchScreenLeft;
+        virtualKey.hitRight =
+                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth +
+                touchScreenLeft;
+        virtualKey.hitTop =
+                (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight +
+                touchScreenTop;
+        virtualKey.hitBottom =
+                (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight +
+                touchScreenTop;
+        mVirtualKeys.push_back(virtualKey);
+    }
+}
+
+void TouchInputMapper::dumpVirtualKeys(std::string& dump) {
+    if (!mVirtualKeys.empty()) {
+        dump += INDENT3 "Virtual Keys:\n";
+
+        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mVirtualKeys[i];
+            dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
+                                         "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+                                 i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft,
+                                 virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+        }
+    }
+}
+
+void TouchInputMapper::parseCalibration() {
+    const PropertyMap& in = getDevice()->getConfiguration();
+    Calibration& out = mCalibration;
+
+    // Size
+    out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+    String8 sizeCalibrationString;
+    if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+        if (sizeCalibrationString == "none") {
+            out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+        } else if (sizeCalibrationString == "geometric") {
+            out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+        } else if (sizeCalibrationString == "diameter") {
+            out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
+        } else if (sizeCalibrationString == "box") {
+            out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
+        } else if (sizeCalibrationString == "area") {
+            out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
+        } else if (sizeCalibrationString != "default") {
+            ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
+        }
+    }
+
+    out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale);
+    out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias);
+    out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
+
+    // Pressure
+    out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+    String8 pressureCalibrationString;
+    if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+        if (pressureCalibrationString == "none") {
+            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+        } else if (pressureCalibrationString == "physical") {
+            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+        } else if (pressureCalibrationString == "amplitude") {
+            out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+        } else if (pressureCalibrationString != "default") {
+            ALOGW("Invalid value for touch.pressure.calibration: '%s'",
+                  pressureCalibrationString.string());
+        }
+    }
+
+    out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
+
+    // Orientation
+    out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+    String8 orientationCalibrationString;
+    if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+        if (orientationCalibrationString == "none") {
+            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+        } else if (orientationCalibrationString == "interpolated") {
+            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+        } else if (orientationCalibrationString == "vector") {
+            out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
+        } else if (orientationCalibrationString != "default") {
+            ALOGW("Invalid value for touch.orientation.calibration: '%s'",
+                  orientationCalibrationString.string());
+        }
+    }
+
+    // Distance
+    out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+    String8 distanceCalibrationString;
+    if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+        if (distanceCalibrationString == "none") {
+            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+        } else if (distanceCalibrationString == "scaled") {
+            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+        } else if (distanceCalibrationString != "default") {
+            ALOGW("Invalid value for touch.distance.calibration: '%s'",
+                  distanceCalibrationString.string());
+        }
+    }
+
+    out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
+
+    out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
+    String8 coverageCalibrationString;
+    if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
+        if (coverageCalibrationString == "none") {
+            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+        } else if (coverageCalibrationString == "box") {
+            out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
+        } else if (coverageCalibrationString != "default") {
+            ALOGW("Invalid value for touch.coverage.calibration: '%s'",
+                  coverageCalibrationString.string());
+        }
+    }
+}
+
+void TouchInputMapper::resolveCalibration() {
+    // Size
+    if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
+        if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
+            mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+        }
+    } else {
+        mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+    }
+
+    // Pressure
+    if (mRawPointerAxes.pressure.valid) {
+        if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
+            mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+        }
+    } else {
+        mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+    }
+
+    // Orientation
+    if (mRawPointerAxes.orientation.valid) {
+        if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
+            mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+        }
+    } else {
+        mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+    }
+
+    // Distance
+    if (mRawPointerAxes.distance.valid) {
+        if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
+            mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+        }
+    } else {
+        mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+    }
+
+    // Coverage
+    if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
+        mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+    }
+}
+
+void TouchInputMapper::dumpCalibration(std::string& dump) {
+    dump += INDENT3 "Calibration:\n";
+
+    // Size
+    switch (mCalibration.sizeCalibration) {
+        case Calibration::SIZE_CALIBRATION_NONE:
+            dump += INDENT4 "touch.size.calibration: none\n";
+            break;
+        case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+            dump += INDENT4 "touch.size.calibration: geometric\n";
+            break;
+        case Calibration::SIZE_CALIBRATION_DIAMETER:
+            dump += INDENT4 "touch.size.calibration: diameter\n";
+            break;
+        case Calibration::SIZE_CALIBRATION_BOX:
+            dump += INDENT4 "touch.size.calibration: box\n";
+            break;
+        case Calibration::SIZE_CALIBRATION_AREA:
+            dump += INDENT4 "touch.size.calibration: area\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    if (mCalibration.haveSizeScale) {
+        dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale);
+    }
+
+    if (mCalibration.haveSizeBias) {
+        dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias);
+    }
+
+    if (mCalibration.haveSizeIsSummed) {
+        dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n",
+                             toString(mCalibration.sizeIsSummed));
+    }
+
+    // Pressure
+    switch (mCalibration.pressureCalibration) {
+        case Calibration::PRESSURE_CALIBRATION_NONE:
+            dump += INDENT4 "touch.pressure.calibration: none\n";
+            break;
+        case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+            dump += INDENT4 "touch.pressure.calibration: physical\n";
+            break;
+        case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+            dump += INDENT4 "touch.pressure.calibration: amplitude\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    if (mCalibration.havePressureScale) {
+        dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale);
+    }
+
+    // Orientation
+    switch (mCalibration.orientationCalibration) {
+        case Calibration::ORIENTATION_CALIBRATION_NONE:
+            dump += INDENT4 "touch.orientation.calibration: none\n";
+            break;
+        case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+            dump += INDENT4 "touch.orientation.calibration: interpolated\n";
+            break;
+        case Calibration::ORIENTATION_CALIBRATION_VECTOR:
+            dump += INDENT4 "touch.orientation.calibration: vector\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    // Distance
+    switch (mCalibration.distanceCalibration) {
+        case Calibration::DISTANCE_CALIBRATION_NONE:
+            dump += INDENT4 "touch.distance.calibration: none\n";
+            break;
+        case Calibration::DISTANCE_CALIBRATION_SCALED:
+            dump += INDENT4 "touch.distance.calibration: scaled\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+
+    if (mCalibration.haveDistanceScale) {
+        dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale);
+    }
+
+    switch (mCalibration.coverageCalibration) {
+        case Calibration::COVERAGE_CALIBRATION_NONE:
+            dump += INDENT4 "touch.coverage.calibration: none\n";
+            break;
+        case Calibration::COVERAGE_CALIBRATION_BOX:
+            dump += INDENT4 "touch.coverage.calibration: box\n";
+            break;
+        default:
+            ALOG_ASSERT(false);
+    }
+}
+
+void TouchInputMapper::dumpAffineTransformation(std::string& dump) {
+    dump += INDENT3 "Affine Transformation:\n";
+
+    dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale);
+    dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix);
+    dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset);
+    dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix);
+    dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale);
+    dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset);
+}
+
+void TouchInputMapper::updateAffineTransformation() {
+    mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
+                                                                 mSurfaceOrientation);
+}
+
+void TouchInputMapper::reset(nsecs_t when) {
+    mCursorButtonAccumulator.reset(getDevice());
+    mCursorScrollAccumulator.reset(getDevice());
+    mTouchButtonAccumulator.reset(getDevice());
+
+    mPointerVelocityControl.reset();
+    mWheelXVelocityControl.reset();
+    mWheelYVelocityControl.reset();
+
+    mRawStatesPending.clear();
+    mCurrentRawState.clear();
+    mCurrentCookedState.clear();
+    mLastRawState.clear();
+    mLastCookedState.clear();
+    mPointerUsage = POINTER_USAGE_NONE;
+    mSentHoverEnter = false;
+    mHavePointerIds = false;
+    mCurrentMotionAborted = false;
+    mDownTime = 0;
+
+    mCurrentVirtualKey.down = false;
+
+    mPointerGesture.reset();
+    mPointerSimple.reset();
+    resetExternalStylus();
+
+    if (mPointerController != nullptr) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->clearSpots();
+    }
+
+    InputMapper::reset(when);
+}
+
+void TouchInputMapper::resetExternalStylus() {
+    mExternalStylusState.clear();
+    mExternalStylusId = -1;
+    mExternalStylusFusionTimeout = LLONG_MAX;
+    mExternalStylusDataPending = false;
+}
+
+void TouchInputMapper::clearStylusDataPendingFlags() {
+    mExternalStylusDataPending = false;
+    mExternalStylusFusionTimeout = LLONG_MAX;
+}
+
+void TouchInputMapper::process(const RawEvent* rawEvent) {
+    mCursorButtonAccumulator.process(rawEvent);
+    mCursorScrollAccumulator.process(rawEvent);
+    mTouchButtonAccumulator.process(rawEvent);
+
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+}
+
+void TouchInputMapper::sync(nsecs_t when) {
+    const RawState* last =
+            mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
+
+    // Push a new state.
+    mRawStatesPending.emplace_back();
+
+    RawState* next = &mRawStatesPending.back();
+    next->clear();
+    next->when = when;
+
+    // Sync button state.
+    next->buttonState =
+            mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
+
+    // Sync scroll
+    next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
+    next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
+    mCursorScrollAccumulator.finishSync();
+
+    // Sync touch
+    syncTouch(when, next);
+
+    // Assign pointer ids.
+    if (!mHavePointerIds) {
+        assignPointerIds(last, next);
+    }
+
+#if DEBUG_RAW_EVENTS
+    ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+          "hovering ids 0x%08x -> 0x%08x",
+          last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
+          last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
+          last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
+#endif
+
+    processRawTouches(false /*timeout*/);
+}
+
+void TouchInputMapper::processRawTouches(bool timeout) {
+    if (mDeviceMode == DEVICE_MODE_DISABLED) {
+        // Drop all input if the device is disabled.
+        mCurrentRawState.clear();
+        mRawStatesPending.clear();
+        return;
+    }
+
+    // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
+    // valid and must go through the full cook and dispatch cycle. This ensures that anything
+    // touching the current state will only observe the events that have been dispatched to the
+    // rest of the pipeline.
+    const size_t N = mRawStatesPending.size();
+    size_t count;
+    for (count = 0; count < N; count++) {
+        const RawState& next = mRawStatesPending[count];
+
+        // A failure to assign the stylus id means that we're waiting on stylus data
+        // and so should defer the rest of the pipeline.
+        if (assignExternalStylusId(next, timeout)) {
+            break;
+        }
+
+        // All ready to go.
+        clearStylusDataPendingFlags();
+        mCurrentRawState.copyFrom(next);
+        if (mCurrentRawState.when < mLastRawState.when) {
+            mCurrentRawState.when = mLastRawState.when;
+        }
+        cookAndDispatch(mCurrentRawState.when);
+    }
+    if (count != 0) {
+        mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
+    }
+
+    if (mExternalStylusDataPending) {
+        if (timeout) {
+            nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
+            clearStylusDataPendingFlags();
+            mCurrentRawState.copyFrom(mLastRawState);
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Timeout expired, synthesizing event with new stylus data");
+#endif
+            cookAndDispatch(when);
+        } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
+            mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+        }
+    }
+}
+
+void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+    // Always start with a clean state.
+    mCurrentCookedState.clear();
+
+    // Apply stylus buttons to current raw state.
+    applyExternalStylusButtonState(when);
+
+    // Handle policy on initial down or hover events.
+    bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
+            mCurrentRawState.rawPointerData.pointerCount != 0;
+
+    uint32_t policyFlags = 0;
+    bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
+    if (initialDown || buttonsPressed) {
+        // If this is a touch screen, hide the pointer on an initial down.
+        if (mDeviceMode == DEVICE_MODE_DIRECT) {
+            getContext()->fadePointer();
+        }
+
+        if (mParameters.wake) {
+            policyFlags |= POLICY_FLAG_WAKE;
+        }
+    }
+
+    // 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)) {
+        mCurrentRawState.rawPointerData.clear();
+    }
+
+    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure
+    // with cooked pointer data that has the same ids and indices as the raw data.
+    // The following code can use either the raw or cooked data, as needed.
+    cookPointerData();
+
+    // Apply stylus pressure to current cooked state.
+    applyExternalStylusTouchState(when);
+
+    // Synthesize key down from raw buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
+                         mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+                         mCurrentCookedState.buttonState);
+
+    // Dispatch the touches either directly or by translation through a pointer on screen.
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+                pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+                mCurrentCookedState.stylusIdBits.markBit(id);
+            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
+                       pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+                mCurrentCookedState.fingerIdBits.markBit(id);
+            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
+                mCurrentCookedState.mouseIdBits.markBit(id);
+            }
+        }
+        for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+                pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+                mCurrentCookedState.stylusIdBits.markBit(id);
+            }
+        }
+
+        // Stylus takes precedence over all tools, then mouse, then finger.
+        PointerUsage pointerUsage = mPointerUsage;
+        if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+            mCurrentCookedState.mouseIdBits.clear();
+            mCurrentCookedState.fingerIdBits.clear();
+            pointerUsage = POINTER_USAGE_STYLUS;
+        } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+            mCurrentCookedState.fingerIdBits.clear();
+            pointerUsage = POINTER_USAGE_MOUSE;
+        } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
+                   isPointerDown(mCurrentRawState.buttonState)) {
+            pointerUsage = POINTER_USAGE_GESTURES;
+        }
+
+        dispatchPointerUsage(when, policyFlags, pointerUsage);
+    } else {
+        if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
+            mPointerController != nullptr) {
+            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+
+            mPointerController->setButtonState(mCurrentRawState.buttonState);
+            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+                                         mCurrentCookedState.cookedPointerData.idToIndex,
+                                         mCurrentCookedState.cookedPointerData.touchingIdBits,
+                                         mViewport.displayId);
+        }
+
+        if (!mCurrentMotionAborted) {
+            dispatchButtonRelease(when, policyFlags);
+            dispatchHoverExit(when, policyFlags);
+            dispatchTouches(when, policyFlags);
+            dispatchHoverEnterAndMove(when, policyFlags);
+            dispatchButtonPress(when, policyFlags);
+        }
+
+        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
+            mCurrentMotionAborted = false;
+        }
+    }
+
+    // Synthesize key up from raw buttons if needed.
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+                         mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+                         mCurrentCookedState.buttonState);
+
+    // Clear some transient state.
+    mCurrentRawState.rawVScroll = 0;
+    mCurrentRawState.rawHScroll = 0;
+
+    // Copy current touch to last touch in preparation for the next cycle.
+    mLastRawState.copyFrom(mCurrentRawState);
+    mLastCookedState.copyFrom(mCurrentCookedState);
+}
+
+void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
+    if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
+        mCurrentRawState.buttonState |= mExternalStylusState.buttons;
+    }
+}
+
+void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
+    CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
+    const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
+
+    if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
+        float pressure = mExternalStylusState.pressure;
+        if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
+            const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
+            pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+        }
+        PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
+        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+
+        PointerProperties& properties =
+                currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
+        if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
+            properties.toolType = mExternalStylusState.toolType;
+        }
+    }
+}
+
+bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
+    if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+        return false;
+    }
+
+    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
+            state.rawPointerData.pointerCount != 0;
+    if (initialDown) {
+        if (mExternalStylusState.pressure != 0.0f) {
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Have both stylus and touch data, beginning fusion");
+#endif
+            mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+        } else if (timeout) {
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Timeout expired, assuming touch is not a stylus.");
+#endif
+            resetExternalStylus();
+        } else {
+            if (mExternalStylusFusionTimeout == LLONG_MAX) {
+                mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
+            }
+#if DEBUG_STYLUS_FUSION
+            ALOGD("No stylus data but stylus is connected, requesting timeout "
+                  "(%" PRId64 "ms)",
+                  mExternalStylusFusionTimeout);
+#endif
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+            return true;
+        }
+    }
+
+    // Check if the stylus pointer has gone up.
+    if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
+#if DEBUG_STYLUS_FUSION
+        ALOGD("Stylus pointer is going up");
+#endif
+        mExternalStylusId = -1;
+    }
+
+    return false;
+}
+
+void TouchInputMapper::timeoutExpired(nsecs_t when) {
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        if (mPointerUsage == POINTER_USAGE_GESTURES) {
+            dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+        }
+    } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+        if (mExternalStylusFusionTimeout < when) {
+            processRawTouches(true /*timeout*/);
+        } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+        }
+    }
+}
+
+void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+    mExternalStylusState.copyFrom(state);
+    if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
+        // We're either in the middle of a fused stream of data or we're waiting on data before
+        // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
+        // data.
+        mExternalStylusDataPending = true;
+        processRawTouches(false /*timeout*/);
+    }
+}
+
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+    // Check for release of a virtual key.
+    if (mCurrentVirtualKey.down) {
+        if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+            // Pointer went up while virtual key was down.
+            mCurrentVirtualKey.down = false;
+            if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+                ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+                      mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+                dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+                                   AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+            }
+            return true;
+        }
+
+        if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
+            uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+            if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
+                // Pointer is still within the space of the virtual key.
+                return true;
+            }
+        }
+
+        // Pointer left virtual key area or another pointer also went down.
+        // Send key cancellation but do not consume the touch yet.
+        // This is useful when the user swipes through from the virtual key area
+        // into the main display surface.
+        mCurrentVirtualKey.down = false;
+        if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+            ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
+                  mCurrentVirtualKey.scanCode);
+#endif
+            dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+                               AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
+                                       AKEY_EVENT_FLAG_CANCELED);
+        }
+    }
+
+    if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() &&
+        !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+        // Pointer just went down.  Check for virtual key press or off-screen touches.
+        uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
+        const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
+        if (!isPointInsideSurface(pointer.x, pointer.y)) {
+            // If exactly one pointer went down, check for virtual key hit.
+            // Otherwise we will drop the entire stroke.
+            if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
+                const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+                if (virtualKey) {
+                    mCurrentVirtualKey.down = true;
+                    mCurrentVirtualKey.downTime = when;
+                    mCurrentVirtualKey.keyCode = virtualKey->keyCode;
+                    mCurrentVirtualKey.scanCode = virtualKey->scanCode;
+                    mCurrentVirtualKey.ignored =
+                            mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode,
+                                                           virtualKey->scanCode);
+
+                    if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+                        ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+                              mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+                        dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN,
+                                           AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                   AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
+    // Disable all virtual key touches that happen within a short time interval of the
+    // most recent touch within the screen area.  The idea is to filter out stray
+    // virtual key presses when interacting with the touch screen.
+    //
+    // Problems we're trying to solve:
+    //
+    // 1. While scrolling a list or dragging the window shade, the user swipes down into a
+    //    virtual key area that is implemented by a separate touch panel and accidentally
+    //    triggers a virtual key.
+    //
+    // 2. While typing in the on screen keyboard, the user taps slightly outside the screen
+    //    area and accidentally triggers a virtual key.  This often happens when virtual keys
+    //    are layed out below the screen near to where the on screen keyboard's space bar
+    //    is displayed.
+    if (mConfig.virtualKeyQuietTime > 0 &&
+        !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
+        mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
+    }
+    return false;
+}
+
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+                                          int32_t keyEventAction, int32_t keyEventFlags) {
+    int32_t keyCode = mCurrentVirtualKey.keyCode;
+    int32_t scanCode = mCurrentVirtualKey.scanCode;
+    nsecs_t downTime = mCurrentVirtualKey.downTime;
+    int32_t metaState = mContext->getGlobalMetaState();
+    policyFlags |= POLICY_FLAG_VIRTUAL;
+
+    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, 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) {
+    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,
+                       mCurrentCookedState.cookedPointerData.pointerProperties,
+                       mCurrentCookedState.cookedPointerData.pointerCoords,
+                       mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        mCurrentMotionAborted = true;
+    }
+}
+
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
+    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
+    int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t buttonState = mCurrentCookedState.buttonState;
+
+    if (currentIdBits == lastIdBits) {
+        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,
+                           mCurrentCookedState.cookedPointerData.pointerProperties,
+                           mCurrentCookedState.cookedPointerData.pointerCoords,
+                           mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        }
+    } else {
+        // There may be pointers going up and pointers going down and pointers moving
+        // all at the same time.
+        BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
+        BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
+        BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
+        BitSet32 dispatchedIdBits(lastIdBits.value);
+
+        // Update last coordinates of pointers that have moved so that we observe the new
+        // pointer positions at the same time as other pointers that have just gone up.
+        bool moveNeeded =
+                updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties,
+                                    mCurrentCookedState.cookedPointerData.pointerCoords,
+                                    mCurrentCookedState.cookedPointerData.idToIndex,
+                                    mLastCookedState.cookedPointerData.pointerProperties,
+                                    mLastCookedState.cookedPointerData.pointerCoords,
+                                    mLastCookedState.cookedPointerData.idToIndex, moveIdBits);
+        if (buttonState != mLastCookedState.buttonState) {
+            moveNeeded = true;
+        }
+
+        // Dispatch pointer up events.
+        while (!upIdBits.isEmpty()) {
+            uint32_t upId = upIdBits.clearFirstMarkedBit();
+
+            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
+                           metaState, buttonState, 0,
+                           mLastCookedState.cookedPointerData.pointerProperties,
+                           mLastCookedState.cookedPointerData.pointerCoords,
+                           mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            dispatchedIdBits.clearBit(upId);
+        }
+
+        // Dispatch move events if any of the remaining pointers moved from their old locations.
+        // Although applications receive new locations as part of individual pointer up
+        // 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,
+                           mCurrentCookedState.cookedPointerData.pointerCoords,
+                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        }
+
+        // Dispatch pointer down events using the new pointer locations.
+        while (!downIdBits.isEmpty()) {
+            uint32_t downId = downIdBits.clearFirstMarkedBit();
+            dispatchedIdBits.markBit(downId);
+
+            if (dispatchedIdBits.count() == 1) {
+                // First pointer is going down.  Set down time.
+                mDownTime = when;
+            }
+
+            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
+                           metaState, buttonState, 0,
+                           mCurrentCookedState.cookedPointerData.pointerProperties,
+                           mCurrentCookedState.cookedPointerData.pointerCoords,
+                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
+                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        }
+    }
+}
+
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, 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,
+                       mLastCookedState.cookedPointerData.pointerProperties,
+                       mLastCookedState.cookedPointerData.pointerCoords,
+                       mLastCookedState.cookedPointerData.idToIndex,
+                       mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
+                       mOrientedYPrecision, mDownTime);
+        mSentHoverEnter = false;
+    }
+}
+
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, 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,
+                           mCurrentCookedState.cookedPointerData.pointerProperties,
+                           mCurrentCookedState.cookedPointerData.pointerCoords,
+                           mCurrentCookedState.cookedPointerData.idToIndex,
+                           mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            mSentHoverEnter = true;
+        }
+
+        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                       mCurrentRawState.buttonState, 0,
+                       mCurrentCookedState.cookedPointerData.pointerProperties,
+                       mCurrentCookedState.cookedPointerData.pointerCoords,
+                       mCurrentCookedState.cookedPointerData.idToIndex,
+                       mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+    }
+}
+
+void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
+    BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
+    const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
+    const int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t buttonState = mLastCookedState.buttonState;
+    while (!releasedButtons.isEmpty()) {
+        int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
+        buttonState &= ~actionButton;
+        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                       actionButton, 0, metaState, buttonState, 0,
+                       mCurrentCookedState.cookedPointerData.pointerProperties,
+                       mCurrentCookedState.cookedPointerData.pointerCoords,
+                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+    }
+}
+
+void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
+    BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
+    const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
+    const int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t buttonState = mLastCookedState.buttonState;
+    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,
+                       mCurrentCookedState.cookedPointerData.pointerProperties,
+                       mCurrentCookedState.cookedPointerData.pointerCoords,
+                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+    }
+}
+
+const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
+    if (!cookedPointerData.touchingIdBits.isEmpty()) {
+        return cookedPointerData.touchingIdBits;
+    }
+    return cookedPointerData.hoveringIdBits;
+}
+
+void TouchInputMapper::cookPointerData() {
+    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
+
+    mCurrentCookedState.cookedPointerData.clear();
+    mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
+    mCurrentCookedState.cookedPointerData.hoveringIdBits =
+            mCurrentRawState.rawPointerData.hoveringIdBits;
+    mCurrentCookedState.cookedPointerData.touchingIdBits =
+            mCurrentRawState.rawPointerData.touchingIdBits;
+
+    if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
+        mCurrentCookedState.buttonState = 0;
+    } else {
+        mCurrentCookedState.buttonState = mCurrentRawState.buttonState;
+    }
+
+    // Walk through the the active pointers and map device coordinates onto
+    // surface coordinates and adjust for display orientation.
+    for (uint32_t i = 0; i < currentPointerCount; i++) {
+        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
+
+        // Size
+        float touchMajor, touchMinor, toolMajor, toolMinor, size;
+        switch (mCalibration.sizeCalibration) {
+            case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+            case Calibration::SIZE_CALIBRATION_DIAMETER:
+            case Calibration::SIZE_CALIBRATION_BOX:
+            case Calibration::SIZE_CALIBRATION_AREA:
+                if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
+                    touchMajor = in.touchMajor;
+                    touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+                    toolMajor = in.toolMajor;
+                    toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+                    size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+                                                            : in.touchMajor;
+                } else if (mRawPointerAxes.touchMajor.valid) {
+                    toolMajor = touchMajor = in.touchMajor;
+                    toolMinor = touchMinor =
+                            mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+                    size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+                                                            : in.touchMajor;
+                } else if (mRawPointerAxes.toolMajor.valid) {
+                    touchMajor = toolMajor = in.toolMajor;
+                    touchMinor = toolMinor =
+                            mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+                    size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor)
+                                                           : in.toolMajor;
+                } else {
+                    ALOG_ASSERT(false,
+                                "No touch or tool axes.  "
+                                "Size calibration should have been resolved to NONE.");
+                    touchMajor = 0;
+                    touchMinor = 0;
+                    toolMajor = 0;
+                    toolMinor = 0;
+                    size = 0;
+                }
+
+                if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
+                    uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count();
+                    if (touchingCount > 1) {
+                        touchMajor /= touchingCount;
+                        touchMinor /= touchingCount;
+                        toolMajor /= touchingCount;
+                        toolMinor /= touchingCount;
+                        size /= touchingCount;
+                    }
+                }
+
+                if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
+                    touchMajor *= mGeometricScale;
+                    touchMinor *= mGeometricScale;
+                    toolMajor *= mGeometricScale;
+                    toolMinor *= mGeometricScale;
+                } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
+                    touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
+                    touchMinor = touchMajor;
+                    toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
+                    toolMinor = toolMajor;
+                } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
+                    touchMinor = touchMajor;
+                    toolMinor = toolMajor;
+                }
+
+                mCalibration.applySizeScaleAndBias(&touchMajor);
+                mCalibration.applySizeScaleAndBias(&touchMinor);
+                mCalibration.applySizeScaleAndBias(&toolMajor);
+                mCalibration.applySizeScaleAndBias(&toolMinor);
+                size *= mSizeScale;
+                break;
+            default:
+                touchMajor = 0;
+                touchMinor = 0;
+                toolMajor = 0;
+                toolMinor = 0;
+                size = 0;
+                break;
+        }
+
+        // Pressure
+        float pressure;
+        switch (mCalibration.pressureCalibration) {
+            case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+            case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+                pressure = in.pressure * mPressureScale;
+                break;
+            default:
+                pressure = in.isHovering ? 0 : 1;
+                break;
+        }
+
+        // Tilt and Orientation
+        float tilt;
+        float orientation;
+        if (mHaveTilt) {
+            float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
+            float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
+            orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
+            tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
+        } else {
+            tilt = 0;
+
+            switch (mCalibration.orientationCalibration) {
+                case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+                    orientation = in.orientation * mOrientationScale;
+                    break;
+                case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+                    int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
+                    int32_t c2 = signExtendNybble(in.orientation & 0x0f);
+                    if (c1 != 0 || c2 != 0) {
+                        orientation = atan2f(c1, c2) * 0.5f;
+                        float confidence = hypotf(c1, c2);
+                        float scale = 1.0f + confidence / 16.0f;
+                        touchMajor *= scale;
+                        touchMinor /= scale;
+                        toolMajor *= scale;
+                        toolMinor /= scale;
+                    } else {
+                        orientation = 0;
+                    }
+                    break;
+                }
+                default:
+                    orientation = 0;
+            }
+        }
+
+        // Distance
+        float distance;
+        switch (mCalibration.distanceCalibration) {
+            case Calibration::DISTANCE_CALIBRATION_SCALED:
+                distance = in.distance * mDistanceScale;
+                break;
+            default:
+                distance = 0;
+        }
+
+        // Coverage
+        int32_t rawLeft, rawTop, rawRight, rawBottom;
+        switch (mCalibration.coverageCalibration) {
+            case Calibration::COVERAGE_CALIBRATION_BOX:
+                rawLeft = (in.toolMinor & 0xffff0000) >> 16;
+                rawRight = in.toolMinor & 0x0000ffff;
+                rawBottom = in.toolMajor & 0x0000ffff;
+                rawTop = (in.toolMajor & 0xffff0000) >> 16;
+                break;
+            default:
+                rawLeft = rawTop = rawRight = rawBottom = 0;
+                break;
+        }
+
+        // Adjust X,Y coords for device calibration
+        // TODO: Adjust coverage coords?
+        float xTransformed = in.x, yTransformed = in.y;
+        mAffineTransform.applyTo(xTransformed, yTransformed);
+
+        // Adjust X, Y, and coverage coords for surface orientation.
+        float x, y;
+        float left, top, right, bottom;
+
+        switch (mSurfaceOrientation) {
+            case DISPLAY_ORIENTATION_90:
+                x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
+                left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
+                top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
+                orientation -= M_PI_2;
+                if (mOrientedRanges.haveOrientation &&
+                    orientation < mOrientedRanges.orientation.min) {
+                    orientation +=
+                            (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+                }
+                break;
+            case DISPLAY_ORIENTATION_180:
+                x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
+                y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
+                left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
+                right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
+                bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
+                top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
+                orientation -= M_PI;
+                if (mOrientedRanges.haveOrientation &&
+                    orientation < mOrientedRanges.orientation.min) {
+                    orientation +=
+                            (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+                }
+                break;
+            case DISPLAY_ORIENTATION_270:
+                x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
+                y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
+                right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
+                bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                orientation += M_PI_2;
+                if (mOrientedRanges.haveOrientation &&
+                    orientation > mOrientedRanges.orientation.max) {
+                    orientation -=
+                            (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+                }
+                break;
+            default:
+                x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                break;
+        }
+
+        // Write output coords.
+        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
+        out.clear();
+        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+        out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
+        out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
+        out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
+        out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
+        out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
+        out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
+        if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
+            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
+            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
+            out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
+        } else {
+            out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
+            out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
+        }
+
+        // Write output properties.
+        PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
+        uint32_t id = in.id;
+        properties.clear();
+        properties.id = id;
+        properties.toolType = in.toolType;
+
+        // Write id index.
+        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
+    }
+}
+
+void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
+                                            PointerUsage pointerUsage) {
+    if (pointerUsage != mPointerUsage) {
+        abortPointerUsage(when, policyFlags);
+        mPointerUsage = pointerUsage;
+    }
+
+    switch (mPointerUsage) {
+        case POINTER_USAGE_GESTURES:
+            dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+            break;
+        case POINTER_USAGE_STYLUS:
+            dispatchPointerStylus(when, policyFlags);
+            break;
+        case POINTER_USAGE_MOUSE:
+            dispatchPointerMouse(when, policyFlags);
+            break;
+        default:
+            break;
+    }
+}
+
+void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
+    switch (mPointerUsage) {
+        case POINTER_USAGE_GESTURES:
+            abortPointerGestures(when, policyFlags);
+            break;
+        case POINTER_USAGE_STYLUS:
+            abortPointerStylus(when, policyFlags);
+            break;
+        case POINTER_USAGE_MOUSE:
+            abortPointerMouse(when, policyFlags);
+            break;
+        default:
+            break;
+    }
+
+    mPointerUsage = POINTER_USAGE_NONE;
+}
+
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
+    // Update current gesture coordinates.
+    bool cancelPreviousGesture, finishPreviousGesture;
+    bool sendEvents =
+            preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
+    if (!sendEvents) {
+        return;
+    }
+    if (finishPreviousGesture) {
+        cancelPreviousGesture = false;
+    }
+
+    // Update the pointer presentation and spots.
+    if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+        if (finishPreviousGesture || cancelPreviousGesture) {
+            mPointerController->clearSpots();
+        }
+
+        if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+            mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+                                         mPointerGesture.currentGestureIdToIndex,
+                                         mPointerGesture.currentGestureIdBits,
+                                         mPointerController->getDisplayId());
+        }
+    } else {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+    }
+
+    // Show or hide the pointer if needed.
+    switch (mPointerGesture.currentGestureMode) {
+        case PointerGesture::NEUTRAL:
+        case PointerGesture::QUIET:
+            if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH &&
+                mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
+                // Remind the user of where the pointer is after finishing a gesture with spots.
+                mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
+            }
+            break;
+        case PointerGesture::TAP:
+        case PointerGesture::TAP_DRAG:
+        case PointerGesture::BUTTON_CLICK_OR_DRAG:
+        case PointerGesture::HOVER:
+        case PointerGesture::PRESS:
+        case PointerGesture::SWIPE:
+            // Unfade the pointer when the current gesture manipulates the
+            // area directly under the pointer.
+            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            break;
+        case PointerGesture::FREEFORM:
+            // Fade the pointer when the current gesture manipulates a different
+            // area and there are spots to guide the user experience.
+            if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+            } else {
+                mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            }
+            break;
+    }
+
+    // Send events!
+    int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t buttonState = mCurrentCookedState.buttonState;
+
+    // Update last coordinates of pointers that have moved so that we observe the new
+    // pointer positions at the same time as other pointers that have just gone up.
+    bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP ||
+            mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG ||
+            mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG ||
+            mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
+            mPointerGesture.currentGestureMode == PointerGesture::SWIPE ||
+            mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+    bool moveNeeded = false;
+    if (down && !cancelPreviousGesture && !finishPreviousGesture &&
+        !mPointerGesture.lastGestureIdBits.isEmpty() &&
+        !mPointerGesture.currentGestureIdBits.isEmpty()) {
+        BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value &
+                                    mPointerGesture.lastGestureIdBits.value);
+        moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties,
+                                         mPointerGesture.currentGestureCoords,
+                                         mPointerGesture.currentGestureIdToIndex,
+                                         mPointerGesture.lastGestureProperties,
+                                         mPointerGesture.lastGestureCoords,
+                                         mPointerGesture.lastGestureIdToIndex, movedGestureIdBits);
+        if (buttonState != mLastCookedState.buttonState) {
+            moveNeeded = true;
+        }
+    }
+
+    // Send motion events for all pointers that went up or were canceled.
+    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,
+                           mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
+                           mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
+                           mPointerGesture.downTime);
+
+            dispatchedGestureIdBits.clear();
+        } else {
+            BitSet32 upGestureIdBits;
+            if (finishPreviousGesture) {
+                upGestureIdBits = dispatchedGestureIdBits;
+            } else {
+                upGestureIdBits.value =
+                        dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value;
+            }
+            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,
+                               mPointerGesture.lastGestureCoords,
+                               mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
+                               0, mPointerGesture.downTime);
+
+                dispatchedGestureIdBits.clearBit(id);
+            }
+        }
+    }
+
+    // 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,
+                       mPointerGesture.currentGestureProperties,
+                       mPointerGesture.currentGestureCoords,
+                       mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
+                       mPointerGesture.downTime);
+    }
+
+    // Send motion events for all pointers that went down.
+    if (down) {
+        BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value &
+                                   ~dispatchedGestureIdBits.value);
+        while (!downGestureIdBits.isEmpty()) {
+            uint32_t id = downGestureIdBits.clearFirstMarkedBit();
+            dispatchedGestureIdBits.markBit(id);
+
+            if (dispatchedGestureIdBits.count() == 1) {
+                mPointerGesture.downTime = when;
+            }
+
+            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
+                           metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
+                           mPointerGesture.currentGestureCoords,
+                           mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
+                           0, mPointerGesture.downTime);
+        }
+    }
+
+    // Send motion events for hover.
+    if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                       mPointerGesture.currentGestureProperties,
+                       mPointerGesture.currentGestureCoords,
+                       mPointerGesture.currentGestureIdToIndex,
+                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
+    } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
+        // Synthesize a hover move event after all pointers go up to indicate that
+        // the pointer is hovering again even if the user is not currently touching
+        // the touch pad.  This ensures that a view will receive a fresh hover enter
+        // event after a tap.
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+
+        PointerProperties pointerProperties;
+        pointerProperties.clear();
+        pointerProperties.id = 0;
+        pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        PointerCoords pointerCoords;
+        pointerCoords.clear();
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+        const int32_t displayId = mPointerController->getDisplayId();
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), 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 */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    // Update state.
+    mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
+    if (!down) {
+        mPointerGesture.lastGestureIdBits.clear();
+    } else {
+        mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
+        for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+            mPointerGesture.lastGestureProperties[index].copyFrom(
+                    mPointerGesture.currentGestureProperties[index]);
+            mPointerGesture.lastGestureCoords[index].copyFrom(
+                    mPointerGesture.currentGestureCoords[index]);
+            mPointerGesture.lastGestureIdToIndex[id] = index;
+        }
+    }
+}
+
+void TouchInputMapper::abortPointerGestures(nsecs_t when, 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,
+                       mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
+                       mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
+                       0, 0, mPointerGesture.downTime);
+    }
+
+    // Reset the current pointer gesture.
+    mPointerGesture.reset();
+    mPointerVelocityControl.reset();
+
+    // Remove any current spots.
+    if (mPointerController != nullptr) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->clearSpots();
+    }
+}
+
+bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
+                                              bool* outFinishPreviousGesture, bool isTimeout) {
+    *outCancelPreviousGesture = false;
+    *outFinishPreviousGesture = false;
+
+    // Handle TAP timeout.
+    if (isTimeout) {
+#if DEBUG_GESTURES
+        ALOGD("Gestures: Processing timeout");
+#endif
+
+        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
+                // The tap/drag timeout has not yet expired.
+                getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime +
+                                                   mConfig.pointerGestureTapDragInterval);
+            } else {
+                // The tap is finished.
+#if DEBUG_GESTURES
+                ALOGD("Gestures: TAP finished");
+#endif
+                *outFinishPreviousGesture = true;
+
+                mPointerGesture.activeGestureId = -1;
+                mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+                mPointerGesture.currentGestureIdBits.clear();
+
+                mPointerVelocityControl.reset();
+                return true;
+            }
+        }
+
+        // We did not handle this timeout.
+        return false;
+    }
+
+    const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+    const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
+
+    // Update the velocity tracker.
+    {
+        VelocityTracker::Position positions[MAX_POINTERS];
+        uint32_t count = 0;
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer =
+                    mCurrentRawState.rawPointerData.pointerForId(id);
+            positions[count].x = pointer.x * mPointerXMovementScale;
+            positions[count].y = pointer.y * mPointerYMovementScale;
+        }
+        mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
+                                                    positions);
+    }
+
+    // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
+    // to NEUTRAL, then we should not generate tap event.
+    if (mPointerGesture.lastGestureMode != PointerGesture::HOVER &&
+        mPointerGesture.lastGestureMode != PointerGesture::TAP &&
+        mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+        mPointerGesture.resetTap();
+    }
+
+    // Pick a new active touch id if needed.
+    // Choose an arbitrary pointer that just went down, if there is one.
+    // Otherwise choose an arbitrary remaining pointer.
+    // This guarantees we always have an active touch id when there is at least one pointer.
+    // We keep the same active touch id for as long as possible.
+    int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
+    int32_t activeTouchId = lastActiveTouchId;
+    if (activeTouchId < 0) {
+        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
+            activeTouchId = mPointerGesture.activeTouchId =
+                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
+            mPointerGesture.firstTouchTime = when;
+        }
+    } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
+        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
+            activeTouchId = mPointerGesture.activeTouchId =
+                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
+        } else {
+            activeTouchId = mPointerGesture.activeTouchId = -1;
+        }
+    }
+
+    // Determine whether we are in quiet time.
+    bool isQuietTime = false;
+    if (activeTouchId < 0) {
+        mPointerGesture.resetQuietTime();
+    } else {
+        isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
+        if (!isQuietTime) {
+            if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
+                 mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
+                 mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) &&
+                currentFingerCount < 2) {
+                // Enter quiet time when exiting swipe or freeform state.
+                // This is to prevent accidentally entering the hover state and flinging the
+                // pointer when finishing a swipe and there is still one pointer left onscreen.
+                isQuietTime = true;
+            } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG &&
+                       currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
+                // Enter quiet time when releasing the button and there are still two or more
+                // fingers down.  This may indicate that one finger was used to press the button
+                // but it has not gone up yet.
+                isQuietTime = true;
+            }
+            if (isQuietTime) {
+                mPointerGesture.quietTime = when;
+            }
+        }
+    }
+
+    // Switch states based on button and pointer state.
+    if (isQuietTime) {
+        // Case 1: Quiet time. (QUIET)
+#if DEBUG_GESTURES
+        ALOGD("Gestures: QUIET for next %0.3fms",
+              (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
+#endif
+        if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+            *outFinishPreviousGesture = true;
+        }
+
+        mPointerGesture.activeGestureId = -1;
+        mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+        mPointerGesture.currentGestureIdBits.clear();
+
+        mPointerVelocityControl.reset();
+    } else if (isPointerDown(mCurrentRawState.buttonState)) {
+        // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
+        // The pointer follows the active touch point.
+        // Emit DOWN, MOVE, UP events at the pointer location.
+        //
+        // Only the active touch matters; other fingers are ignored.  This policy helps
+        // to handle the case where the user places a second finger on the touch pad
+        // to apply the necessary force to depress an integrated button below the surface.
+        // We don't want the second finger to be delivered to applications.
+        //
+        // For this to work well, we need to make sure to track the pointer that is really
+        // active.  If the user first puts one finger down to click then adds another
+        // finger to drag then the active pointer should switch to the finger that is
+        // being dragged.
+#if DEBUG_GESTURES
+        ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+              "currentFingerCount=%d",
+              activeTouchId, currentFingerCount);
+#endif
+        // Reset state when just starting.
+        if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+            *outFinishPreviousGesture = true;
+            mPointerGesture.activeGestureId = 0;
+        }
+
+        // Switch pointers if needed.
+        // Find the fastest pointer and follow it.
+        if (activeTouchId >= 0 && currentFingerCount > 1) {
+            int32_t bestId = -1;
+            float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
+            for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
+                uint32_t id = idBits.clearFirstMarkedBit();
+                float vx, vy;
+                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
+                    float speed = hypotf(vx, vy);
+                    if (speed > bestSpeed) {
+                        bestId = id;
+                        bestSpeed = speed;
+                    }
+                }
+            }
+            if (bestId >= 0 && bestId != activeTouchId) {
+                mPointerGesture.activeTouchId = activeTouchId = bestId;
+#if DEBUG_GESTURES
+                ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+                      "bestId=%d, bestSpeed=%0.3f",
+                      bestId, bestSpeed);
+#endif
+            }
+        }
+
+        float deltaX = 0, deltaY = 0;
+        if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
+            const RawPointerData::Pointer& currentPointer =
+                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
+            const RawPointerData::Pointer& lastPointer =
+                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
+            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
+            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
+
+            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+            // Move the pointer using a relative motion.
+            // When using spots, the click will occur at the position of the anchor
+            // spot and all other spots will move there.
+            mPointerController->move(deltaX, deltaY);
+        } else {
+            mPointerVelocityControl.reset();
+        }
+
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+
+        mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+        mPointerGesture.currentGestureIdBits.clear();
+        mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+        mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+        mPointerGesture.currentGestureProperties[0].clear();
+        mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+        mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+        mPointerGesture.currentGestureCoords[0].clear();
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+    } else if (currentFingerCount == 0) {
+        // Case 3. No fingers down and button is not pressed. (NEUTRAL)
+        if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+            *outFinishPreviousGesture = true;
+        }
+
+        // Watch for taps coming out of HOVER or TAP_DRAG mode.
+        // Checking for taps after TAP_DRAG allows us to detect double-taps.
+        bool tapped = false;
+        if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
+             mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
+            lastFingerCount == 1) {
+            if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
+                float x, y;
+                mPointerController->getPosition(&x, &y);
+                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+                    fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: TAP");
+#endif
+
+                    mPointerGesture.tapUpTime = when;
+                    getContext()->requestTimeoutAtTime(when +
+                                                       mConfig.pointerGestureTapDragInterval);
+
+                    mPointerGesture.activeGestureId = 0;
+                    mPointerGesture.currentGestureMode = PointerGesture::TAP;
+                    mPointerGesture.currentGestureIdBits.clear();
+                    mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+                    mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+                    mPointerGesture.currentGestureProperties[0].clear();
+                    mPointerGesture.currentGestureProperties[0].id =
+                            mPointerGesture.activeGestureId;
+                    mPointerGesture.currentGestureProperties[0].toolType =
+                            AMOTION_EVENT_TOOL_TYPE_FINGER;
+                    mPointerGesture.currentGestureCoords[0].clear();
+                    mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+                                                                         mPointerGesture.tapX);
+                    mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+                                                                         mPointerGesture.tapY);
+                    mPointerGesture.currentGestureCoords[0]
+                            .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+                    tapped = true;
+                } else {
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
+                          y - mPointerGesture.tapY);
+#endif
+                }
+            } else {
+#if DEBUG_GESTURES
+                if (mPointerGesture.tapDownTime != LLONG_MIN) {
+                    ALOGD("Gestures: Not a TAP, %0.3fms since down",
+                          (when - mPointerGesture.tapDownTime) * 0.000001f);
+                } else {
+                    ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+                }
+#endif
+            }
+        }
+
+        mPointerVelocityControl.reset();
+
+        if (!tapped) {
+#if DEBUG_GESTURES
+            ALOGD("Gestures: NEUTRAL");
+#endif
+            mPointerGesture.activeGestureId = -1;
+            mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+            mPointerGesture.currentGestureIdBits.clear();
+        }
+    } else if (currentFingerCount == 1) {
+        // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
+        // The pointer follows the active touch point.
+        // When in HOVER, emit HOVER_MOVE events at the pointer location.
+        // When in TAP_DRAG, emit MOVE events at the pointer location.
+        ALOG_ASSERT(activeTouchId >= 0);
+
+        mPointerGesture.currentGestureMode = PointerGesture::HOVER;
+        if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+            if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
+                float x, y;
+                mPointerController->getPosition(&x, &y);
+                if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+                    fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+                    mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+                } else {
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+                          x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+#endif
+                }
+            } else {
+#if DEBUG_GESTURES
+                ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+                      (when - mPointerGesture.tapUpTime) * 0.000001f);
+#endif
+            }
+        } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
+            mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+        }
+
+        float deltaX = 0, deltaY = 0;
+        if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
+            const RawPointerData::Pointer& currentPointer =
+                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
+            const RawPointerData::Pointer& lastPointer =
+                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
+            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
+            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
+
+            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+            // Move the pointer using a relative motion.
+            // When using spots, the hover or drag will occur at the position of the anchor spot.
+            mPointerController->move(deltaX, deltaY);
+        } else {
+            mPointerVelocityControl.reset();
+        }
+
+        bool down;
+        if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+#if DEBUG_GESTURES
+            ALOGD("Gestures: TAP_DRAG");
+#endif
+            down = true;
+        } else {
+#if DEBUG_GESTURES
+            ALOGD("Gestures: HOVER");
+#endif
+            if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+                *outFinishPreviousGesture = true;
+            }
+            mPointerGesture.activeGestureId = 0;
+            down = false;
+        }
+
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+
+        mPointerGesture.currentGestureIdBits.clear();
+        mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+        mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+        mPointerGesture.currentGestureProperties[0].clear();
+        mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+        mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+        mPointerGesture.currentGestureCoords[0].clear();
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+                                                             down ? 1.0f : 0.0f);
+
+        if (lastFingerCount == 0 && currentFingerCount != 0) {
+            mPointerGesture.resetTap();
+            mPointerGesture.tapDownTime = when;
+            mPointerGesture.tapX = x;
+            mPointerGesture.tapY = y;
+        }
+    } else {
+        // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
+        // We need to provide feedback for each finger that goes down so we cannot wait
+        // for the fingers to move before deciding what to do.
+        //
+        // The ambiguous case is deciding what to do when there are two fingers down but they
+        // have not moved enough to determine whether they are part of a drag or part of a
+        // freeform gesture, or just a press or long-press at the pointer location.
+        //
+        // When there are two fingers we start with the PRESS hypothesis and we generate a
+        // down at the pointer location.
+        //
+        // When the two fingers move enough or when additional fingers are added, we make
+        // a decision to transition into SWIPE or FREEFORM mode accordingly.
+        ALOG_ASSERT(activeTouchId >= 0);
+
+        bool settled = when >=
+                mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
+        if (mPointerGesture.lastGestureMode != PointerGesture::PRESS &&
+            mPointerGesture.lastGestureMode != PointerGesture::SWIPE &&
+            mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+            *outFinishPreviousGesture = true;
+        } else if (!settled && currentFingerCount > lastFingerCount) {
+            // Additional pointers have gone down but not yet settled.
+            // Reset the gesture.
+#if DEBUG_GESTURES
+            ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
+                  "settle time remaining %0.3fms",
+                  (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+                   when) * 0.000001f);
+#endif
+            *outCancelPreviousGesture = true;
+        } else {
+            // Continue previous gesture.
+            mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+        }
+
+        if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
+            mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+            mPointerGesture.activeGestureId = 0;
+            mPointerGesture.referenceIdBits.clear();
+            mPointerVelocityControl.reset();
+
+            // Use the centroid and pointer location as the reference points for the gesture.
+#if DEBUG_GESTURES
+            ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+                  "settle time remaining %0.3fms",
+                  (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+                   when) * 0.000001f);
+#endif
+            mCurrentRawState.rawPointerData
+                    .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
+                                                   &mPointerGesture.referenceTouchY);
+            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+                                            &mPointerGesture.referenceGestureY);
+        }
+
+        // Clear the reference deltas for fingers not yet included in the reference calculation.
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
+                             ~mPointerGesture.referenceIdBits.value);
+             !idBits.isEmpty();) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            mPointerGesture.referenceDeltas[id].dx = 0;
+            mPointerGesture.referenceDeltas[id].dy = 0;
+        }
+        mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
+
+        // Add delta for all fingers and calculate a common movement delta.
+        float commonDeltaX = 0, commonDeltaY = 0;
+        BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
+                              mCurrentCookedState.fingerIdBits.value);
+        for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
+            bool first = (idBits == commonIdBits);
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
+            const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
+            PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+            delta.dx += cpd.x - lpd.x;
+            delta.dy += cpd.y - lpd.y;
+
+            if (first) {
+                commonDeltaX = delta.dx;
+                commonDeltaY = delta.dy;
+            } else {
+                commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+                commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
+            }
+        }
+
+        // Consider transitions from PRESS to SWIPE or MULTITOUCH.
+        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+            float dist[MAX_POINTER_ID + 1];
+            int32_t distOverThreshold = 0;
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+                uint32_t id = idBits.clearFirstMarkedBit();
+                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+                dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
+                if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
+                    distOverThreshold += 1;
+                }
+            }
+
+            // Only transition when at least two pointers have moved further than
+            // the minimum distance threshold.
+            if (distOverThreshold >= 2) {
+                if (currentFingerCount > 2) {
+                    // There are more than two pointers, switch to FREEFORM.
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+                          currentFingerCount);
+#endif
+                    *outCancelPreviousGesture = true;
+                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                } else {
+                    // There are exactly two pointers.
+                    BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+                    uint32_t id1 = idBits.clearFirstMarkedBit();
+                    uint32_t id2 = idBits.firstMarkedBit();
+                    const RawPointerData::Pointer& p1 =
+                            mCurrentRawState.rawPointerData.pointerForId(id1);
+                    const RawPointerData::Pointer& p2 =
+                            mCurrentRawState.rawPointerData.pointerForId(id2);
+                    float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
+                    if (mutualDistance > mPointerGestureMaxSwipeWidth) {
+                        // There are two pointers but they are too far apart for a SWIPE,
+                        // switch to FREEFORM.
+#if DEBUG_GESTURES
+                        ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+                              mutualDistance, mPointerGestureMaxSwipeWidth);
+#endif
+                        *outCancelPreviousGesture = true;
+                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                    } else {
+                        // There are two pointers.  Wait for both pointers to start moving
+                        // before deciding whether this is a SWIPE or FREEFORM gesture.
+                        float dist1 = dist[id1];
+                        float dist2 = dist[id2];
+                        if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
+                            dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
+                            // Calculate the dot product of the displacement vectors.
+                            // When the vectors are oriented in approximately the same direction,
+                            // the angle betweeen them is near zero and the cosine of the angle
+                            // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) *
+                            // mag(v2).
+                            PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+                            PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+                            float dx1 = delta1.dx * mPointerXZoomScale;
+                            float dy1 = delta1.dy * mPointerYZoomScale;
+                            float dx2 = delta2.dx * mPointerXZoomScale;
+                            float dy2 = delta2.dy * mPointerYZoomScale;
+                            float dot = dx1 * dx2 + dy1 * dy2;
+                            float cosine = dot / (dist1 * dist2); // denominator always > 0
+                            if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
+                                // Pointers are moving in the same direction.  Switch to SWIPE.
+#if DEBUG_GESTURES
+                                ALOGD("Gestures: PRESS transitioned to SWIPE, "
+                                      "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                      "cosine %0.3f >= %0.3f",
+                                      dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+                                      mConfig.pointerGestureMultitouchMinDistance, cosine,
+                                      mConfig.pointerGestureSwipeTransitionAngleCosine);
+#endif
+                                mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+                            } else {
+                                // Pointers are moving in different directions.  Switch to FREEFORM.
+#if DEBUG_GESTURES
+                                ALOGD("Gestures: PRESS transitioned to FREEFORM, "
+                                      "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                      "cosine %0.3f < %0.3f",
+                                      dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+                                      mConfig.pointerGestureMultitouchMinDistance, cosine,
+                                      mConfig.pointerGestureSwipeTransitionAngleCosine);
+#endif
+                                *outCancelPreviousGesture = true;
+                                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                            }
+                        }
+                    }
+                }
+            }
+        } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+            // Switch from SWIPE to FREEFORM if additional pointers go down.
+            // Cancel previous gesture.
+            if (currentFingerCount > 2) {
+#if DEBUG_GESTURES
+                ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+                      currentFingerCount);
+#endif
+                *outCancelPreviousGesture = true;
+                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+            }
+        }
+
+        // Move the reference points based on the overall group motion of the fingers
+        // except in PRESS mode while waiting for a transition to occur.
+        if (mPointerGesture.currentGestureMode != PointerGesture::PRESS &&
+            (commonDeltaX || commonDeltaY)) {
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+                uint32_t id = idBits.clearFirstMarkedBit();
+                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+                delta.dx = 0;
+                delta.dy = 0;
+            }
+
+            mPointerGesture.referenceTouchX += commonDeltaX;
+            mPointerGesture.referenceTouchY += commonDeltaY;
+
+            commonDeltaX *= mPointerXMovementScale;
+            commonDeltaY *= mPointerYMovementScale;
+
+            rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
+            mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
+
+            mPointerGesture.referenceGestureX += commonDeltaX;
+            mPointerGesture.referenceGestureY += commonDeltaY;
+        }
+
+        // Report gestures.
+        if (mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
+            mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+            // PRESS or SWIPE mode.
+#if DEBUG_GESTURES
+            ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
+                  "activeGestureId=%d, currentTouchPointerCount=%d",
+                  activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+#endif
+            ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+            mPointerGesture.currentGestureIdBits.clear();
+            mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+            mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+            mPointerGesture.currentGestureProperties[0].clear();
+            mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+            mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+            mPointerGesture.currentGestureCoords[0].clear();
+            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+                                                                 mPointerGesture.referenceGestureX);
+            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+                                                                 mPointerGesture.referenceGestureY);
+            mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+        } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+            // FREEFORM mode.
+#if DEBUG_GESTURES
+            ALOGD("Gestures: FREEFORM activeTouchId=%d,"
+                  "activeGestureId=%d, currentTouchPointerCount=%d",
+                  activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+#endif
+            ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+            mPointerGesture.currentGestureIdBits.clear();
+
+            BitSet32 mappedTouchIdBits;
+            BitSet32 usedGestureIdBits;
+            if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+                // Initially, assign the active gesture id to the active touch point
+                // if there is one.  No other touch id bits are mapped yet.
+                if (!*outCancelPreviousGesture) {
+                    mappedTouchIdBits.markBit(activeTouchId);
+                    usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
+                    mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
+                            mPointerGesture.activeGestureId;
+                } else {
+                    mPointerGesture.activeGestureId = -1;
+                }
+            } else {
+                // Otherwise, assume we mapped all touches from the previous frame.
+                // Reuse all mappings that are still applicable.
+                mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value &
+                        mCurrentCookedState.fingerIdBits.value;
+                usedGestureIdBits = mPointerGesture.lastGestureIdBits;
+
+                // Check whether we need to choose a new active gesture id because the
+                // current went went up.
+                for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
+                                            ~mCurrentCookedState.fingerIdBits.value);
+                     !upTouchIdBits.isEmpty();) {
+                    uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
+                    uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
+                    if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
+                        mPointerGesture.activeGestureId = -1;
+                        break;
+                    }
+                }
+            }
+
+#if DEBUG_GESTURES
+            ALOGD("Gestures: FREEFORM follow up "
+                  "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+                  "activeGestureId=%d",
+                  mappedTouchIdBits.value, usedGestureIdBits.value,
+                  mPointerGesture.activeGestureId);
+#endif
+
+            BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+            for (uint32_t i = 0; i < currentFingerCount; i++) {
+                uint32_t touchId = idBits.clearFirstMarkedBit();
+                uint32_t gestureId;
+                if (!mappedTouchIdBits.hasBit(touchId)) {
+                    gestureId = usedGestureIdBits.markFirstUnmarkedBit();
+                    mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: FREEFORM "
+                          "new mapping for touch id %d -> gesture id %d",
+                          touchId, gestureId);
+#endif
+                } else {
+                    gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
+#if DEBUG_GESTURES
+                    ALOGD("Gestures: FREEFORM "
+                          "existing mapping for touch id %d -> gesture id %d",
+                          touchId, gestureId);
+#endif
+                }
+                mPointerGesture.currentGestureIdBits.markBit(gestureId);
+                mPointerGesture.currentGestureIdToIndex[gestureId] = i;
+
+                const RawPointerData::Pointer& pointer =
+                        mCurrentRawState.rawPointerData.pointerForId(touchId);
+                float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
+                float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
+                rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+
+                mPointerGesture.currentGestureProperties[i].clear();
+                mPointerGesture.currentGestureProperties[i].id = gestureId;
+                mPointerGesture.currentGestureProperties[i].toolType =
+                        AMOTION_EVENT_TOOL_TYPE_FINGER;
+                mPointerGesture.currentGestureCoords[i].clear();
+                mPointerGesture.currentGestureCoords[i]
+                        .setAxisValue(AMOTION_EVENT_AXIS_X,
+                                      mPointerGesture.referenceGestureX + deltaX);
+                mPointerGesture.currentGestureCoords[i]
+                        .setAxisValue(AMOTION_EVENT_AXIS_Y,
+                                      mPointerGesture.referenceGestureY + deltaY);
+                mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+                                                                     1.0f);
+            }
+
+            if (mPointerGesture.activeGestureId < 0) {
+                mPointerGesture.activeGestureId =
+                        mPointerGesture.currentGestureIdBits.firstMarkedBit();
+#if DEBUG_GESTURES
+                ALOGD("Gestures: FREEFORM new "
+                      "activeGestureId=%d",
+                      mPointerGesture.activeGestureId);
+#endif
+            }
+        }
+    }
+
+    mPointerController->setButtonState(mCurrentRawState.buttonState);
+
+#if DEBUG_GESTURES
+    ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+          "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+          "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+          toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+          mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+          mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+    for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+        const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
+        const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+        ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
+              "x=%0.3f, y=%0.3f, pressure=%0.3f",
+              id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+              coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+              coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+    }
+    for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+        const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
+        const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+        ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
+              "x=%0.3f, y=%0.3f, pressure=%0.3f",
+              id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+              coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+              coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+    }
+#endif
+    return true;
+}
+
+void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
+    mPointerSimple.currentCoords.clear();
+    mPointerSimple.currentProperties.clear();
+
+    bool down, hovering;
+    if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
+        uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
+        uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
+        float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
+        float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
+        mPointerController->setPosition(x, y);
+
+        hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
+        down = !hovering;
+
+        mPointerController->getPosition(&x, &y);
+        mPointerSimple.currentCoords.copyFrom(
+                mCurrentCookedState.cookedPointerData.pointerCoords[index]);
+        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerSimple.currentProperties.id = 0;
+        mPointerSimple.currentProperties.toolType =
+                mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType;
+    } else {
+        down = false;
+        hovering = false;
+    }
+
+    dispatchPointerSimple(when, policyFlags, down, hovering);
+}
+
+void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
+    abortPointerSimple(when, policyFlags);
+}
+
+void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
+    mPointerSimple.currentCoords.clear();
+    mPointerSimple.currentProperties.clear();
+
+    bool down, hovering;
+    if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
+        uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
+        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+        float deltaX = 0, deltaY = 0;
+        if (mLastCookedState.mouseIdBits.hasBit(id)) {
+            uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
+            deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x -
+                      mLastRawState.rawPointerData.pointers[lastIndex].x) *
+                    mPointerXMovementScale;
+            deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y -
+                      mLastRawState.rawPointerData.pointers[lastIndex].y) *
+                    mPointerYMovementScale;
+
+            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+            mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+            mPointerController->move(deltaX, deltaY);
+        } else {
+            mPointerVelocityControl.reset();
+        }
+
+        down = isPointerDown(mCurrentRawState.buttonState);
+        hovering = !down;
+
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+        mPointerSimple.currentCoords.copyFrom(
+                mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
+        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+                                                  hovering ? 0.0f : 1.0f);
+        mPointerSimple.currentProperties.id = 0;
+        mPointerSimple.currentProperties.toolType =
+                mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType;
+    } else {
+        mPointerVelocityControl.reset();
+
+        down = false;
+        hovering = false;
+    }
+
+    dispatchPointerSimple(when, policyFlags, down, hovering);
+}
+
+void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
+    abortPointerSimple(when, policyFlags);
+
+    mPointerVelocityControl.reset();
+}
+
+void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
+                                             bool hovering) {
+    int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t displayId = mViewport.displayId;
+
+    if (down || hovering) {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+        mPointerController->clearSpots();
+        mPointerController->setButtonState(mCurrentRawState.buttonState);
+        mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+    } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+    }
+    displayId = mPointerController->getDisplayId();
+
+    float xCursorPosition;
+    float yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+    if (mPointerSimple.down && !down) {
+        mPointerSimple.down = false;
+
+        // Send up.
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    if (mPointerSimple.hovering && !hovering) {
+        mPointerSimple.hovering = false;
+
+        // Send hover exit.
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    if (down) {
+        if (!mPointerSimple.down) {
+            mPointerSimple.down = true;
+            mPointerSimple.downTime = when;
+
+            // Send down.
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
+        }
+
+        // Send move.
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    if (hovering) {
+        if (!mPointerSimple.hovering) {
+            mPointerSimple.hovering = true;
+
+            // Send hover enter.
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
+        }
+
+        // Send hover move.
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
+        float vscroll = mCurrentRawState.rawVScroll;
+        float hscroll = mCurrentRawState.rawHScroll;
+        mWheelYVelocityControl.move(when, nullptr, &vscroll);
+        mWheelXVelocityControl.move(when, &hscroll, nullptr);
+
+        // Send scroll.
+        PointerCoords pointerCoords;
+        pointerCoords.copyFrom(mPointerSimple.currentCoords);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, 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,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
+    }
+
+    // Save state.
+    if (down || hovering) {
+        mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
+        mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+    } else {
+        mPointerSimple.reset();
+    }
+}
+
+void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
+    mPointerSimple.currentCoords.clear();
+    mPointerSimple.currentProperties.clear();
+
+    dispatchPointerSimple(when, 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,
+                                      const PointerCoords* coords, const uint32_t* idToIndex,
+                                      BitSet32 idBits, int32_t changedId, float xPrecision,
+                                      float yPrecision, nsecs_t downTime) {
+    PointerCoords pointerCoords[MAX_POINTERS];
+    PointerProperties pointerProperties[MAX_POINTERS];
+    uint32_t pointerCount = 0;
+    while (!idBits.isEmpty()) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        uint32_t index = idToIndex[id];
+        pointerProperties[pointerCount].copyFrom(properties[index]);
+        pointerCoords[pointerCount].copyFrom(coords[index]);
+
+        if (changedId >= 0 && id == uint32_t(changedId)) {
+            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+        }
+
+        pointerCount += 1;
+    }
+
+    ALOG_ASSERT(pointerCount != 0);
+
+    if (changedId >= 0 && pointerCount == 1) {
+        // Replace initial down and final up action.
+        // We can compare the action without masking off the changed pointer index
+        // because we know the index is 0.
+        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+            action = AMOTION_EVENT_ACTION_DOWN;
+        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
+            action = AMOTION_EVENT_ACTION_UP;
+        } else {
+            // Can't happen.
+            ALOG_ASSERT(false);
+        }
+    }
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    }
+    const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
+    const int32_t deviceId = getDeviceId();
+    std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+    std::for_each(frames.begin(), frames.end(),
+                  [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId,
+                          policyFlags, action, actionButton, flags, metaState, buttonState,
+                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                          downTime, std::move(frames));
+    getListener()->notifyMotion(&args);
+}
+
+bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
+                                           const PointerCoords* inCoords,
+                                           const uint32_t* inIdToIndex,
+                                           PointerProperties* outProperties,
+                                           PointerCoords* outCoords, const uint32_t* outIdToIndex,
+                                           BitSet32 idBits) const {
+    bool changed = false;
+    while (!idBits.isEmpty()) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        uint32_t inIndex = inIdToIndex[id];
+        uint32_t outIndex = outIdToIndex[id];
+
+        const PointerProperties& curInProperties = inProperties[inIndex];
+        const PointerCoords& curInCoords = inCoords[inIndex];
+        PointerProperties& curOutProperties = outProperties[outIndex];
+        PointerCoords& curOutCoords = outCoords[outIndex];
+
+        if (curInProperties != curOutProperties) {
+            curOutProperties.copyFrom(curInProperties);
+            changed = true;
+        }
+
+        if (curInCoords != curOutCoords) {
+            curOutCoords.copyFrom(curInCoords);
+            changed = true;
+        }
+    }
+    return changed;
+}
+
+void TouchInputMapper::fadePointer() {
+    if (mPointerController != nullptr) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+    }
+}
+
+void TouchInputMapper::cancelTouch(nsecs_t when) {
+    abortPointerUsage(when, 0 /*policyFlags*/);
+    abortTouches(when, 0 /* policyFlags*/);
+}
+
+bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+    const float scaledX = x * mXScale;
+    const float scaledY = y * mYScale;
+    return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
+            scaledX >= mSurfaceLeft && scaledX <= mSurfaceLeft + mSurfaceWidth &&
+            y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
+            scaledY >= mSurfaceTop && scaledY <= mSurfaceTop + mSurfaceHeight;
+}
+
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
+    for (const VirtualKey& virtualKey : mVirtualKeys) {
+#if DEBUG_VIRTUAL_KEYS
+        ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+              "left=%d, top=%d, right=%d, bottom=%d",
+              x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop,
+              virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+        if (virtualKey.isHit(x, y)) {
+            return &virtualKey;
+        }
+    }
+
+    return nullptr;
+}
+
+void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
+    uint32_t currentPointerCount = current->rawPointerData.pointerCount;
+    uint32_t lastPointerCount = last->rawPointerData.pointerCount;
+
+    current->rawPointerData.clearIdBits();
+
+    if (currentPointerCount == 0) {
+        // No pointers to assign.
+        return;
+    }
+
+    if (lastPointerCount == 0) {
+        // All pointers are new.
+        for (uint32_t i = 0; i < currentPointerCount; i++) {
+            uint32_t id = i;
+            current->rawPointerData.pointers[i].id = id;
+            current->rawPointerData.idToIndex[id] = i;
+            current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
+        }
+        return;
+    }
+
+    if (currentPointerCount == 1 && lastPointerCount == 1 &&
+        current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) {
+        // Only one pointer and no change in count so it must have the same id as before.
+        uint32_t id = last->rawPointerData.pointers[0].id;
+        current->rawPointerData.pointers[0].id = id;
+        current->rawPointerData.idToIndex[id] = 0;
+        current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
+        return;
+    }
+
+    // General case.
+    // We build a heap of squared euclidean distances between current and last pointers
+    // associated with the current and last pointer indices.  Then, we find the best
+    // match (by distance) for each current pointer.
+    // The pointers must have the same tool type but it is possible for them to
+    // transition from hovering to touching or vice-versa while retaining the same id.
+    PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+    uint32_t heapSize = 0;
+    for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+         currentPointerIndex++) {
+        for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+             lastPointerIndex++) {
+            const RawPointerData::Pointer& currentPointer =
+                    current->rawPointerData.pointers[currentPointerIndex];
+            const RawPointerData::Pointer& lastPointer =
+                    last->rawPointerData.pointers[lastPointerIndex];
+            if (currentPointer.toolType == lastPointer.toolType) {
+                int64_t deltaX = currentPointer.x - lastPointer.x;
+                int64_t deltaY = currentPointer.y - lastPointer.y;
+
+                uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+                // Insert new element into the heap (sift up).
+                heap[heapSize].currentPointerIndex = currentPointerIndex;
+                heap[heapSize].lastPointerIndex = lastPointerIndex;
+                heap[heapSize].distance = distance;
+                heapSize += 1;
+            }
+        }
+    }
+
+    // Heapify
+    for (uint32_t startIndex = heapSize / 2; startIndex != 0;) {
+        startIndex -= 1;
+        for (uint32_t parentIndex = startIndex;;) {
+            uint32_t childIndex = parentIndex * 2 + 1;
+            if (childIndex >= heapSize) {
+                break;
+            }
+
+            if (childIndex + 1 < heapSize &&
+                heap[childIndex + 1].distance < heap[childIndex].distance) {
+                childIndex += 1;
+            }
+
+            if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                break;
+            }
+
+            swap(heap[parentIndex], heap[childIndex]);
+            parentIndex = childIndex;
+        }
+    }
+
+#if DEBUG_POINTER_ASSIGNMENT
+    ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+    for (size_t i = 0; i < heapSize; i++) {
+        ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+              heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+    }
+#endif
+
+    // Pull matches out by increasing order of distance.
+    // To avoid reassigning pointers that have already been matched, the loop keeps track
+    // of which last and current pointers have been matched using the matchedXXXBits variables.
+    // It also tracks the used pointer id bits.
+    BitSet32 matchedLastBits(0);
+    BitSet32 matchedCurrentBits(0);
+    BitSet32 usedIdBits(0);
+    bool first = true;
+    for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
+        while (heapSize > 0) {
+            if (first) {
+                // The first time through the loop, we just consume the root element of
+                // the heap (the one with smallest distance).
+                first = false;
+            } else {
+                // Previous iterations consumed the root element of the heap.
+                // Pop root element off of the heap (sift down).
+                heap[0] = heap[heapSize];
+                for (uint32_t parentIndex = 0;;) {
+                    uint32_t childIndex = parentIndex * 2 + 1;
+                    if (childIndex >= heapSize) {
+                        break;
+                    }
+
+                    if (childIndex + 1 < heapSize &&
+                        heap[childIndex + 1].distance < heap[childIndex].distance) {
+                        childIndex += 1;
+                    }
+
+                    if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                        break;
+                    }
+
+                    swap(heap[parentIndex], heap[childIndex]);
+                    parentIndex = childIndex;
+                }
+
+#if DEBUG_POINTER_ASSIGNMENT
+                ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+                for (size_t i = 0; i < heapSize; i++) {
+                    ALOGD("  heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+                          heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+                }
+#endif
+            }
+
+            heapSize -= 1;
+
+            uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+            if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+            uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+            if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+            matchedCurrentBits.markBit(currentPointerIndex);
+            matchedLastBits.markBit(lastPointerIndex);
+
+            uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
+            current->rawPointerData.pointers[currentPointerIndex].id = id;
+            current->rawPointerData.idToIndex[id] = currentPointerIndex;
+            current->rawPointerData.markIdBit(id,
+                                              current->rawPointerData.isHovering(
+                                                      currentPointerIndex));
+            usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+            ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+                  ", distance=%" PRIu64,
+                  lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+            break;
+        }
+    }
+
+    // Assign fresh ids to pointers that were not matched in the process.
+    for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
+        uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
+        uint32_t id = usedIdBits.markFirstUnmarkedBit();
+
+        current->rawPointerData.pointers[currentPointerIndex].id = id;
+        current->rawPointerData.idToIndex[id] = currentPointerIndex;
+        current->rawPointerData.markIdBit(id,
+                                          current->rawPointerData.isHovering(currentPointerIndex));
+
+#if DEBUG_POINTER_ASSIGNMENT
+        ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
+#endif
+    }
+}
+
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+    if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
+        return AKEY_STATE_VIRTUAL;
+    }
+
+    for (const VirtualKey& virtualKey : mVirtualKeys) {
+        if (virtualKey.keyCode == keyCode) {
+            return AKEY_STATE_UP;
+        }
+    }
+
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+    if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
+        return AKEY_STATE_VIRTUAL;
+    }
+
+    for (const VirtualKey& virtualKey : mVirtualKeys) {
+        if (virtualKey.scanCode == scanCode) {
+            return AKEY_STATE_UP;
+        }
+    }
+
+    return AKEY_STATE_UNKNOWN;
+}
+
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                             const int32_t* keyCodes, uint8_t* outFlags) {
+    for (const VirtualKey& virtualKey : mVirtualKeys) {
+        for (size_t i = 0; i < numCodes; i++) {
+            if (virtualKey.keyCode == keyCodes[i]) {
+                outFlags[i] = 1;
+            }
+        }
+    }
+
+    return true;
+}
+
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            return std::make_optional(mViewport.displayId);
+        }
+    }
+    return std::nullopt;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
new file mode 100644
index 0000000..89c017d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+
+#include "CursorButtonAccumulator.h"
+#include "CursorScrollAccumulator.h"
+#include "EventHub.h"
+#include "InputMapper.h"
+#include "InputReaderBase.h"
+#include "TouchButtonAccumulator.h"
+
+#include <stdint.h>
+
+namespace android {
+
+/* Raw axis information from the driver. */
+struct RawPointerAxes {
+    RawAbsoluteAxisInfo x;
+    RawAbsoluteAxisInfo y;
+    RawAbsoluteAxisInfo pressure;
+    RawAbsoluteAxisInfo touchMajor;
+    RawAbsoluteAxisInfo touchMinor;
+    RawAbsoluteAxisInfo toolMajor;
+    RawAbsoluteAxisInfo toolMinor;
+    RawAbsoluteAxisInfo orientation;
+    RawAbsoluteAxisInfo distance;
+    RawAbsoluteAxisInfo tiltX;
+    RawAbsoluteAxisInfo tiltY;
+    RawAbsoluteAxisInfo trackingId;
+    RawAbsoluteAxisInfo slot;
+
+    RawPointerAxes();
+    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
+    void clear();
+};
+
+/* Raw data for a collection of pointers including a pointer id mapping table. */
+struct RawPointerData {
+    struct Pointer {
+        uint32_t id;
+        int32_t x;
+        int32_t y;
+        int32_t pressure;
+        int32_t touchMajor;
+        int32_t touchMinor;
+        int32_t toolMajor;
+        int32_t toolMinor;
+        int32_t orientation;
+        int32_t distance;
+        int32_t tiltX;
+        int32_t tiltY;
+        int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
+        bool isHovering;
+    };
+
+    uint32_t pointerCount;
+    Pointer pointers[MAX_POINTERS];
+    BitSet32 hoveringIdBits, touchingIdBits;
+    uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+    RawPointerData();
+    void clear();
+    void copyFrom(const RawPointerData& other);
+    void getCentroidOfTouchingPointers(float* outX, float* outY) const;
+
+    inline void markIdBit(uint32_t id, bool isHovering) {
+        if (isHovering) {
+            hoveringIdBits.markBit(id);
+        } else {
+            touchingIdBits.markBit(id);
+        }
+    }
+
+    inline void clearIdBits() {
+        hoveringIdBits.clear();
+        touchingIdBits.clear();
+    }
+
+    inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; }
+
+    inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; }
+};
+
+/* Cooked data for a collection of pointers including a pointer id mapping table. */
+struct CookedPointerData {
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    BitSet32 hoveringIdBits, touchingIdBits;
+    uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+    CookedPointerData();
+    void clear();
+    void copyFrom(const CookedPointerData& other);
+
+    inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
+        return pointerCoords[idToIndex[id]];
+    }
+
+    inline PointerCoords& editPointerCoordsWithId(uint32_t id) {
+        return pointerCoords[idToIndex[id]];
+    }
+
+    inline PointerProperties& editPointerPropertiesWithId(uint32_t id) {
+        return pointerProperties[idToIndex[id]];
+    }
+
+    inline bool isHovering(uint32_t pointerIndex) const {
+        return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
+    }
+
+    inline bool isTouching(uint32_t pointerIndex) const {
+        return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
+    }
+};
+
+class TouchInputMapper : public InputMapper {
+public:
+    explicit TouchInputMapper(InputDevice* device);
+    virtual ~TouchInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+    virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
+
+    virtual void fadePointer() override;
+    virtual void cancelTouch(nsecs_t when) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void updateExternalStylusState(const StylusState& state) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
+
+protected:
+    CursorButtonAccumulator mCursorButtonAccumulator;
+    CursorScrollAccumulator mCursorScrollAccumulator;
+    TouchButtonAccumulator mTouchButtonAccumulator;
+
+    struct VirtualKey {
+        int32_t keyCode;
+        int32_t scanCode;
+        uint32_t flags;
+
+        // computed hit box, specified in touch screen coords based on known display size
+        int32_t hitLeft;
+        int32_t hitTop;
+        int32_t hitRight;
+        int32_t hitBottom;
+
+        inline bool isHit(int32_t x, int32_t y) const {
+            return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+        }
+    };
+
+    // Input sources and device mode.
+    uint32_t mSource;
+
+    enum DeviceMode {
+        DEVICE_MODE_DISABLED,   // input is disabled
+        DEVICE_MODE_DIRECT,     // direct mapping (touchscreen)
+        DEVICE_MODE_UNSCALED,   // unscaled mapping (touchpad)
+        DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
+        DEVICE_MODE_POINTER,    // pointer mapping (pointer)
+    };
+    DeviceMode mDeviceMode;
+
+    // The reader's configuration.
+    InputReaderConfiguration mConfig;
+
+    // Immutable configuration parameters.
+    struct Parameters {
+        enum DeviceType {
+            DEVICE_TYPE_TOUCH_SCREEN,
+            DEVICE_TYPE_TOUCH_PAD,
+            DEVICE_TYPE_TOUCH_NAVIGATION,
+            DEVICE_TYPE_POINTER,
+        };
+
+        DeviceType deviceType;
+        bool hasAssociatedDisplay;
+        bool associatedDisplayIsExternal;
+        bool orientationAware;
+        bool hasButtonUnderPad;
+        std::string uniqueDisplayId;
+
+        enum GestureMode {
+            GESTURE_MODE_SINGLE_TOUCH,
+            GESTURE_MODE_MULTI_TOUCH,
+        };
+        GestureMode gestureMode;
+
+        bool wake;
+    } mParameters;
+
+    // Immutable calibration parameters in parsed form.
+    struct Calibration {
+        // Size
+        enum SizeCalibration {
+            SIZE_CALIBRATION_DEFAULT,
+            SIZE_CALIBRATION_NONE,
+            SIZE_CALIBRATION_GEOMETRIC,
+            SIZE_CALIBRATION_DIAMETER,
+            SIZE_CALIBRATION_BOX,
+            SIZE_CALIBRATION_AREA,
+        };
+
+        SizeCalibration sizeCalibration;
+
+        bool haveSizeScale;
+        float sizeScale;
+        bool haveSizeBias;
+        float sizeBias;
+        bool haveSizeIsSummed;
+        bool sizeIsSummed;
+
+        // Pressure
+        enum PressureCalibration {
+            PRESSURE_CALIBRATION_DEFAULT,
+            PRESSURE_CALIBRATION_NONE,
+            PRESSURE_CALIBRATION_PHYSICAL,
+            PRESSURE_CALIBRATION_AMPLITUDE,
+        };
+
+        PressureCalibration pressureCalibration;
+        bool havePressureScale;
+        float pressureScale;
+
+        // Orientation
+        enum OrientationCalibration {
+            ORIENTATION_CALIBRATION_DEFAULT,
+            ORIENTATION_CALIBRATION_NONE,
+            ORIENTATION_CALIBRATION_INTERPOLATED,
+            ORIENTATION_CALIBRATION_VECTOR,
+        };
+
+        OrientationCalibration orientationCalibration;
+
+        // Distance
+        enum DistanceCalibration {
+            DISTANCE_CALIBRATION_DEFAULT,
+            DISTANCE_CALIBRATION_NONE,
+            DISTANCE_CALIBRATION_SCALED,
+        };
+
+        DistanceCalibration distanceCalibration;
+        bool haveDistanceScale;
+        float distanceScale;
+
+        enum CoverageCalibration {
+            COVERAGE_CALIBRATION_DEFAULT,
+            COVERAGE_CALIBRATION_NONE,
+            COVERAGE_CALIBRATION_BOX,
+        };
+
+        CoverageCalibration coverageCalibration;
+
+        inline void applySizeScaleAndBias(float* outSize) const {
+            if (haveSizeScale) {
+                *outSize *= sizeScale;
+            }
+            if (haveSizeBias) {
+                *outSize += sizeBias;
+            }
+            if (*outSize < 0) {
+                *outSize = 0;
+            }
+        }
+    } mCalibration;
+
+    // Affine location transformation/calibration
+    struct TouchAffineTransformation mAffineTransform;
+
+    RawPointerAxes mRawPointerAxes;
+
+    struct RawState {
+        nsecs_t when;
+
+        // Raw pointer sample data.
+        RawPointerData rawPointerData;
+
+        int32_t buttonState;
+
+        // Scroll state.
+        int32_t rawVScroll;
+        int32_t rawHScroll;
+
+        void copyFrom(const RawState& other) {
+            when = other.when;
+            rawPointerData.copyFrom(other.rawPointerData);
+            buttonState = other.buttonState;
+            rawVScroll = other.rawVScroll;
+            rawHScroll = other.rawHScroll;
+        }
+
+        void clear() {
+            when = 0;
+            rawPointerData.clear();
+            buttonState = 0;
+            rawVScroll = 0;
+            rawHScroll = 0;
+        }
+    };
+
+    struct CookedState {
+        // Cooked pointer sample data.
+        CookedPointerData cookedPointerData;
+
+        // Id bits used to differentiate fingers, stylus and mouse tools.
+        BitSet32 fingerIdBits;
+        BitSet32 stylusIdBits;
+        BitSet32 mouseIdBits;
+
+        int32_t buttonState;
+
+        void copyFrom(const CookedState& other) {
+            cookedPointerData.copyFrom(other.cookedPointerData);
+            fingerIdBits = other.fingerIdBits;
+            stylusIdBits = other.stylusIdBits;
+            mouseIdBits = other.mouseIdBits;
+            buttonState = other.buttonState;
+        }
+
+        void clear() {
+            cookedPointerData.clear();
+            fingerIdBits.clear();
+            stylusIdBits.clear();
+            mouseIdBits.clear();
+            buttonState = 0;
+        }
+    };
+
+    std::vector<RawState> mRawStatesPending;
+    RawState mCurrentRawState;
+    CookedState mCurrentCookedState;
+    RawState mLastRawState;
+    CookedState mLastCookedState;
+
+    // State provided by an external stylus
+    StylusState mExternalStylusState;
+    int64_t mExternalStylusId;
+    nsecs_t mExternalStylusFusionTimeout;
+    bool mExternalStylusDataPending;
+
+    // True if we sent a HOVER_ENTER event.
+    bool mSentHoverEnter;
+
+    // Have we assigned pointer IDs for this stream
+    bool mHavePointerIds;
+
+    // Is the current stream of direct touch events aborted
+    bool mCurrentMotionAborted;
+
+    // The time the primary pointer last went down.
+    nsecs_t mDownTime;
+
+    // The pointer controller, or null if the device is not a pointer.
+    sp<PointerControllerInterface> mPointerController;
+
+    std::vector<VirtualKey> mVirtualKeys;
+
+    virtual void configureParameters();
+    virtual void dumpParameters(std::string& dump);
+    virtual void configureRawPointerAxes();
+    virtual void dumpRawPointerAxes(std::string& dump);
+    virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
+    virtual void dumpSurface(std::string& dump);
+    virtual void configureVirtualKeys();
+    virtual void dumpVirtualKeys(std::string& dump);
+    virtual void parseCalibration();
+    virtual void resolveCalibration();
+    virtual void dumpCalibration(std::string& dump);
+    virtual void updateAffineTransformation();
+    virtual void dumpAffineTransformation(std::string& dump);
+    virtual void resolveExternalStylusPresence();
+    virtual bool hasStylus() const = 0;
+    virtual bool hasExternalStylus() const;
+
+    virtual void syncTouch(nsecs_t when, RawState* outState) = 0;
+
+private:
+    // The current viewport.
+    // The components of the viewport are specified in the display's rotated orientation.
+    DisplayViewport mViewport;
+
+    // The surface orientation, width and height set by configureSurface().
+    // The width and height are derived from the viewport but are specified
+    // in the natural orientation.
+    // The surface origin specifies how the surface coordinates should be translated
+    // to align with the logical display coordinate space.
+    int32_t mSurfaceWidth;
+    int32_t mSurfaceHeight;
+    int32_t mSurfaceLeft;
+    int32_t mSurfaceTop;
+
+    // Similar to the surface coordinates, but in the raw display coordinate space rather than in
+    // the logical coordinate space.
+    int32_t mPhysicalWidth;
+    int32_t mPhysicalHeight;
+    int32_t mPhysicalLeft;
+    int32_t mPhysicalTop;
+
+    // The orientation may be different from the viewport orientation as it specifies
+    // the rotation of the surface coordinates required to produce the viewport's
+    // requested orientation, so it will depend on whether the device is orientation aware.
+    int32_t mSurfaceOrientation;
+
+    // Translation and scaling factors, orientation-independent.
+    float mXTranslate;
+    float mXScale;
+    float mXPrecision;
+
+    float mYTranslate;
+    float mYScale;
+    float mYPrecision;
+
+    float mGeometricScale;
+
+    float mPressureScale;
+
+    float mSizeScale;
+
+    float mOrientationScale;
+
+    float mDistanceScale;
+
+    bool mHaveTilt;
+    float mTiltXCenter;
+    float mTiltXScale;
+    float mTiltYCenter;
+    float mTiltYScale;
+
+    bool mExternalStylusConnected;
+
+    // Oriented motion ranges for input device info.
+    struct OrientedRanges {
+        InputDeviceInfo::MotionRange x;
+        InputDeviceInfo::MotionRange y;
+        InputDeviceInfo::MotionRange pressure;
+
+        bool haveSize;
+        InputDeviceInfo::MotionRange size;
+
+        bool haveTouchSize;
+        InputDeviceInfo::MotionRange touchMajor;
+        InputDeviceInfo::MotionRange touchMinor;
+
+        bool haveToolSize;
+        InputDeviceInfo::MotionRange toolMajor;
+        InputDeviceInfo::MotionRange toolMinor;
+
+        bool haveOrientation;
+        InputDeviceInfo::MotionRange orientation;
+
+        bool haveDistance;
+        InputDeviceInfo::MotionRange distance;
+
+        bool haveTilt;
+        InputDeviceInfo::MotionRange tilt;
+
+        OrientedRanges() { clear(); }
+
+        void clear() {
+            haveSize = false;
+            haveTouchSize = false;
+            haveToolSize = false;
+            haveOrientation = false;
+            haveDistance = false;
+            haveTilt = false;
+        }
+    } mOrientedRanges;
+
+    // Oriented dimensions and precision.
+    float mOrientedXPrecision;
+    float mOrientedYPrecision;
+
+    struct CurrentVirtualKeyState {
+        bool down;
+        bool ignored;
+        nsecs_t downTime;
+        int32_t keyCode;
+        int32_t scanCode;
+    } mCurrentVirtualKey;
+
+    // Scale factor for gesture or mouse based pointer movements.
+    float mPointerXMovementScale;
+    float mPointerYMovementScale;
+
+    // Scale factor for gesture based zooming and other freeform motions.
+    float mPointerXZoomScale;
+    float mPointerYZoomScale;
+
+    // The maximum swipe width.
+    float mPointerGestureMaxSwipeWidth;
+
+    struct PointerDistanceHeapElement {
+        uint32_t currentPointerIndex : 8;
+        uint32_t lastPointerIndex : 8;
+        uint64_t distance : 48; // squared distance
+    };
+
+    enum PointerUsage {
+        POINTER_USAGE_NONE,
+        POINTER_USAGE_GESTURES,
+        POINTER_USAGE_STYLUS,
+        POINTER_USAGE_MOUSE,
+    };
+    PointerUsage mPointerUsage;
+
+    struct PointerGesture {
+        enum Mode {
+            // No fingers, button is not pressed.
+            // Nothing happening.
+            NEUTRAL,
+
+            // No fingers, button is not pressed.
+            // Tap detected.
+            // Emits DOWN and UP events at the pointer location.
+            TAP,
+
+            // Exactly one finger dragging following a tap.
+            // Pointer follows the active finger.
+            // Emits DOWN, MOVE and UP events at the pointer location.
+            //
+            // Detect double-taps when the finger goes up while in TAP_DRAG mode.
+            TAP_DRAG,
+
+            // Button is pressed.
+            // Pointer follows the active finger if there is one.  Other fingers are ignored.
+            // Emits DOWN, MOVE and UP events at the pointer location.
+            BUTTON_CLICK_OR_DRAG,
+
+            // Exactly one finger, button is not pressed.
+            // Pointer follows the active finger.
+            // Emits HOVER_MOVE events at the pointer location.
+            //
+            // Detect taps when the finger goes up while in HOVER mode.
+            HOVER,
+
+            // Exactly two fingers but neither have moved enough to clearly indicate
+            // whether a swipe or freeform gesture was intended.  We consider the
+            // pointer to be pressed so this enables clicking or long-pressing on buttons.
+            // Pointer does not move.
+            // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
+            PRESS,
+
+            // Exactly two fingers moving in the same direction, button is not pressed.
+            // Pointer does not move.
+            // Emits DOWN, MOVE and UP events with a single pointer coordinate that
+            // follows the midpoint between both fingers.
+            SWIPE,
+
+            // Two or more fingers moving in arbitrary directions, button is not pressed.
+            // Pointer does not move.
+            // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
+            // each finger individually relative to the initial centroid of the finger.
+            FREEFORM,
+
+            // Waiting for quiet time to end before starting the next gesture.
+            QUIET,
+        };
+
+        // Time the first finger went down.
+        nsecs_t firstTouchTime;
+
+        // The active pointer id from the raw touch data.
+        int32_t activeTouchId; // -1 if none
+
+        // The active pointer id from the gesture last delivered to the application.
+        int32_t activeGestureId; // -1 if none
+
+        // Pointer coords and ids for the current and previous pointer gesture.
+        Mode currentGestureMode;
+        BitSet32 currentGestureIdBits;
+        uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
+        PointerProperties currentGestureProperties[MAX_POINTERS];
+        PointerCoords currentGestureCoords[MAX_POINTERS];
+
+        Mode lastGestureMode;
+        BitSet32 lastGestureIdBits;
+        uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
+        PointerProperties lastGestureProperties[MAX_POINTERS];
+        PointerCoords lastGestureCoords[MAX_POINTERS];
+
+        // Time the pointer gesture last went down.
+        nsecs_t downTime;
+
+        // Time when the pointer went down for a TAP.
+        nsecs_t tapDownTime;
+
+        // Time when the pointer went up for a TAP.
+        nsecs_t tapUpTime;
+
+        // Location of initial tap.
+        float tapX, tapY;
+
+        // Time we started waiting for quiescence.
+        nsecs_t quietTime;
+
+        // Reference points for multitouch gestures.
+        float referenceTouchX; // reference touch X/Y coordinates in surface units
+        float referenceTouchY;
+        float referenceGestureX; // reference gesture X/Y coordinates in pixels
+        float referenceGestureY;
+
+        // Distance that each pointer has traveled which has not yet been
+        // subsumed into the reference gesture position.
+        BitSet32 referenceIdBits;
+        struct Delta {
+            float dx, dy;
+        };
+        Delta referenceDeltas[MAX_POINTER_ID + 1];
+
+        // Describes how touch ids are mapped to gesture ids for freeform gestures.
+        uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
+
+        // A velocity tracker for determining whether to switch active pointers during drags.
+        VelocityTracker velocityTracker;
+
+        void reset() {
+            firstTouchTime = LLONG_MIN;
+            activeTouchId = -1;
+            activeGestureId = -1;
+            currentGestureMode = NEUTRAL;
+            currentGestureIdBits.clear();
+            lastGestureMode = NEUTRAL;
+            lastGestureIdBits.clear();
+            downTime = 0;
+            velocityTracker.clear();
+            resetTap();
+            resetQuietTime();
+        }
+
+        void resetTap() {
+            tapDownTime = LLONG_MIN;
+            tapUpTime = LLONG_MIN;
+        }
+
+        void resetQuietTime() { quietTime = LLONG_MIN; }
+    } mPointerGesture;
+
+    struct PointerSimple {
+        PointerCoords currentCoords;
+        PointerProperties currentProperties;
+        PointerCoords lastCoords;
+        PointerProperties lastProperties;
+
+        // True if the pointer is down.
+        bool down;
+
+        // True if the pointer is hovering.
+        bool hovering;
+
+        // Time the pointer last went down.
+        nsecs_t downTime;
+
+        void reset() {
+            currentCoords.clear();
+            currentProperties.clear();
+            lastCoords.clear();
+            lastProperties.clear();
+            down = false;
+            hovering = false;
+            downTime = 0;
+        }
+    } mPointerSimple;
+
+    // The pointer and scroll velocity controls.
+    VelocityControl mPointerVelocityControl;
+    VelocityControl mWheelXVelocityControl;
+    VelocityControl mWheelYVelocityControl;
+
+    std::optional<DisplayViewport> findViewport();
+
+    void resetExternalStylus();
+    void clearStylusDataPendingFlags();
+
+    void sync(nsecs_t when);
+
+    bool consumeRawTouches(nsecs_t when, 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 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);
+    const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
+    void cookPointerData();
+    void abortTouches(nsecs_t when, uint32_t policyFlags);
+
+    void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
+    void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
+
+    void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
+    void abortPointerGestures(nsecs_t when, 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 dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
+    void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
+
+    void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering);
+    void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+
+    bool assignExternalStylusId(const RawState& state, bool timeout);
+    void applyExternalStylusButtonState(nsecs_t when);
+    void applyExternalStylusTouchState(nsecs_t when);
+
+    // Dispatches a motion event.
+    // 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,
+                        const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+
+    // Updates pointer coords and properties for pointers with specified ids that have moved.
+    // Returns true if any of them changed.
+    bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords,
+                             const uint32_t* inIdToIndex, PointerProperties* outProperties,
+                             PointerCoords* outCoords, const uint32_t* outIdToIndex,
+                             BitSet32 idBits) const;
+
+    bool isPointInsideSurface(int32_t x, int32_t y);
+    const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
+
+    static void assignPointerIds(const RawState* last, RawState* current);
+
+    const char* modeToString(DeviceMode deviceMode);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
new file mode 100644
index 0000000..a27fab4
--- /dev/null
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include "VibratorInputMapper.h"
+
+namespace android {
+
+VibratorInputMapper::VibratorInputMapper(InputDevice* device)
+      : InputMapper(device), mVibrating(false) {}
+
+VibratorInputMapper::~VibratorInputMapper() {}
+
+uint32_t VibratorInputMapper::getSources() {
+    return 0;
+}
+
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    info->setVibrator(true);
+}
+
+void VibratorInputMapper::process(const RawEvent* rawEvent) {
+    // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+}
+
+void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                                  int32_t token) {
+#if DEBUG_VIBRATOR
+    std::string patternStr;
+    for (size_t i = 0; i < patternSize; i++) {
+        if (i != 0) {
+            patternStr += ", ";
+        }
+        patternStr += StringPrintf("%" PRId64, pattern[i]);
+    }
+    ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
+          patternStr.c_str(), repeat, token);
+#endif
+
+    mVibrating = true;
+    memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
+    mPatternSize = patternSize;
+    mRepeat = repeat;
+    mToken = token;
+    mIndex = -1;
+
+    nextStep();
+}
+
+void VibratorInputMapper::cancelVibrate(int32_t token) {
+#if DEBUG_VIBRATOR
+    ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+#endif
+
+    if (mVibrating && mToken == token) {
+        stopVibrating();
+    }
+}
+
+void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+    if (mVibrating) {
+        if (when >= mNextStepTime) {
+            nextStep();
+        } else {
+            getContext()->requestTimeoutAtTime(mNextStepTime);
+        }
+    }
+}
+
+void VibratorInputMapper::nextStep() {
+    mIndex += 1;
+    if (size_t(mIndex) >= mPatternSize) {
+        if (mRepeat < 0) {
+            // We are done.
+            stopVibrating();
+            return;
+        }
+        mIndex = mRepeat;
+    }
+
+    bool vibratorOn = mIndex & 1;
+    nsecs_t duration = mPattern[mIndex];
+    if (vibratorOn) {
+#if DEBUG_VIBRATOR
+        ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+#endif
+        getEventHub()->vibrate(getDeviceId(), duration);
+    } else {
+#if DEBUG_VIBRATOR
+        ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+        getEventHub()->cancelVibrate(getDeviceId());
+    }
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    mNextStepTime = now + duration;
+    getContext()->requestTimeoutAtTime(mNextStepTime);
+#if DEBUG_VIBRATOR
+    ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+#endif
+}
+
+void VibratorInputMapper::stopVibrating() {
+    mVibrating = false;
+#if DEBUG_VIBRATOR
+    ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+    getEventHub()->cancelVibrate(getDeviceId());
+}
+
+void VibratorInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Vibrator Input Mapper:\n";
+    dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
new file mode 100644
index 0000000..dc67890
--- /dev/null
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class VibratorInputMapper : public InputMapper {
+public:
+    explicit VibratorInputMapper(InputDevice* device);
+    virtual ~VibratorInputMapper();
+
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void process(const RawEvent* rawEvent) override;
+
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                         int32_t token) override;
+    virtual void cancelVibrate(int32_t token) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void dump(std::string& dump) override;
+
+private:
+    bool mVibrating;
+    nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
+    size_t mPatternSize;
+    ssize_t mRepeat;
+    int32_t mToken;
+    ssize_t mIndex;
+    nsecs_t mNextStepTime;
+
+    void nextStep();
+    void stopVibrating();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
new file mode 100644
index 0000000..0337d51
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CursorButtonAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+CursorButtonAccumulator::CursorButtonAccumulator() {
+    clearButtons();
+}
+
+void CursorButtonAccumulator::reset(InputDevice* device) {
+    mBtnLeft = device->isKeyPressed(BTN_LEFT);
+    mBtnRight = device->isKeyPressed(BTN_RIGHT);
+    mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
+    mBtnBack = device->isKeyPressed(BTN_BACK);
+    mBtnSide = device->isKeyPressed(BTN_SIDE);
+    mBtnForward = device->isKeyPressed(BTN_FORWARD);
+    mBtnExtra = device->isKeyPressed(BTN_EXTRA);
+    mBtnTask = device->isKeyPressed(BTN_TASK);
+}
+
+void CursorButtonAccumulator::clearButtons() {
+    mBtnLeft = 0;
+    mBtnRight = 0;
+    mBtnMiddle = 0;
+    mBtnBack = 0;
+    mBtnSide = 0;
+    mBtnForward = 0;
+    mBtnExtra = 0;
+    mBtnTask = 0;
+}
+
+void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_KEY) {
+        switch (rawEvent->code) {
+            case BTN_LEFT:
+                mBtnLeft = rawEvent->value;
+                break;
+            case BTN_RIGHT:
+                mBtnRight = rawEvent->value;
+                break;
+            case BTN_MIDDLE:
+                mBtnMiddle = rawEvent->value;
+                break;
+            case BTN_BACK:
+                mBtnBack = rawEvent->value;
+                break;
+            case BTN_SIDE:
+                mBtnSide = rawEvent->value;
+                break;
+            case BTN_FORWARD:
+                mBtnForward = rawEvent->value;
+                break;
+            case BTN_EXTRA:
+                mBtnExtra = rawEvent->value;
+                break;
+            case BTN_TASK:
+                mBtnTask = rawEvent->value;
+                break;
+        }
+    }
+}
+
+uint32_t CursorButtonAccumulator::getButtonState() const {
+    uint32_t result = 0;
+    if (mBtnLeft) {
+        result |= AMOTION_EVENT_BUTTON_PRIMARY;
+    }
+    if (mBtnRight) {
+        result |= AMOTION_EVENT_BUTTON_SECONDARY;
+    }
+    if (mBtnMiddle) {
+        result |= AMOTION_EVENT_BUTTON_TERTIARY;
+    }
+    if (mBtnBack || mBtnSide) {
+        result |= AMOTION_EVENT_BUTTON_BACK;
+    }
+    if (mBtnForward || mBtnExtra) {
+        result |= AMOTION_EVENT_BUTTON_FORWARD;
+    }
+    return result;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
new file mode 100644
index 0000000..d912310
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of mouse or touch pad buttons. */
+class CursorButtonAccumulator {
+public:
+    CursorButtonAccumulator();
+    void reset(InputDevice* device);
+
+    void process(const RawEvent* rawEvent);
+
+    uint32_t getButtonState() const;
+
+private:
+    bool mBtnLeft;
+    bool mBtnRight;
+    bool mBtnMiddle;
+    bool mBtnBack;
+    bool mBtnSide;
+    bool mBtnForward;
+    bool mBtnExtra;
+    bool mBtnTask;
+
+    void clearButtons();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
new file mode 100644
index 0000000..d744096
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CursorScrollAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) {
+    clearRelativeAxes();
+}
+
+void CursorScrollAccumulator::configure(InputDevice* device) {
+    mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
+    mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
+}
+
+void CursorScrollAccumulator::reset(InputDevice* device) {
+    clearRelativeAxes();
+}
+
+void CursorScrollAccumulator::clearRelativeAxes() {
+    mRelWheel = 0;
+    mRelHWheel = 0;
+}
+
+void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_REL) {
+        switch (rawEvent->code) {
+            case REL_WHEEL:
+                mRelWheel = rawEvent->value;
+                break;
+            case REL_HWHEEL:
+                mRelHWheel = rawEvent->value;
+                break;
+        }
+    }
+}
+
+void CursorScrollAccumulator::finishSync() {
+    clearRelativeAxes();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
new file mode 100644
index 0000000..85f331f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of cursor scrolling motions. */
+
+class CursorScrollAccumulator {
+public:
+    CursorScrollAccumulator();
+    void configure(InputDevice* device);
+    void reset(InputDevice* device);
+
+    void process(const RawEvent* rawEvent);
+    void finishSync();
+
+    inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
+    inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
+
+    inline int32_t getRelativeX() const { return mRelX; }
+    inline int32_t getRelativeY() const { return mRelY; }
+    inline int32_t getRelativeVWheel() const { return mRelWheel; }
+    inline int32_t getRelativeHWheel() const { return mRelHWheel; }
+
+private:
+    bool mHaveRelWheel;
+    bool mHaveRelHWheel;
+
+    int32_t mRelX;
+    int32_t mRelY;
+    int32_t mRelWheel;
+    int32_t mRelHWheel;
+
+    void clearRelativeAxes();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
new file mode 100644
index 0000000..e9ba727
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SingleTouchMotionAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
+    clearAbsoluteAxes();
+}
+
+void SingleTouchMotionAccumulator::reset(InputDevice* device) {
+    mAbsX = device->getAbsoluteAxisValue(ABS_X);
+    mAbsY = device->getAbsoluteAxisValue(ABS_Y);
+    mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
+    mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
+    mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
+    mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
+    mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
+}
+
+void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
+    mAbsX = 0;
+    mAbsY = 0;
+    mAbsPressure = 0;
+    mAbsToolWidth = 0;
+    mAbsDistance = 0;
+    mAbsTiltX = 0;
+    mAbsTiltY = 0;
+}
+
+void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_ABS) {
+        switch (rawEvent->code) {
+            case ABS_X:
+                mAbsX = rawEvent->value;
+                break;
+            case ABS_Y:
+                mAbsY = rawEvent->value;
+                break;
+            case ABS_PRESSURE:
+                mAbsPressure = rawEvent->value;
+                break;
+            case ABS_TOOL_WIDTH:
+                mAbsToolWidth = rawEvent->value;
+                break;
+            case ABS_DISTANCE:
+                mAbsDistance = rawEvent->value;
+                break;
+            case ABS_TILT_X:
+                mAbsTiltX = rawEvent->value;
+                break;
+            case ABS_TILT_Y:
+                mAbsTiltY = rawEvent->value;
+                break;
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
new file mode 100644
index 0000000..75f8a96
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of single-touch protocol. */
+class SingleTouchMotionAccumulator {
+public:
+    SingleTouchMotionAccumulator();
+
+    void process(const RawEvent* rawEvent);
+    void reset(InputDevice* device);
+
+    inline int32_t getAbsoluteX() const { return mAbsX; }
+    inline int32_t getAbsoluteY() const { return mAbsY; }
+    inline int32_t getAbsolutePressure() const { return mAbsPressure; }
+    inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; }
+    inline int32_t getAbsoluteDistance() const { return mAbsDistance; }
+    inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; }
+    inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; }
+
+private:
+    int32_t mAbsX;
+    int32_t mAbsY;
+    int32_t mAbsPressure;
+    int32_t mAbsToolWidth;
+    int32_t mAbsDistance;
+    int32_t mAbsTiltX;
+    int32_t mAbsTiltY;
+
+    void clearAbsoluteAxes();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
new file mode 100644
index 0000000..d2f06c8
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TouchButtonAccumulator.h"
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
+    clearButtons();
+}
+
+void TouchButtonAccumulator::configure(InputDevice* device) {
+    mHaveBtnTouch = device->hasKey(BTN_TOUCH);
+    mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) ||
+            device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) ||
+            device->hasKey(BTN_TOOL_AIRBRUSH);
+}
+
+void TouchButtonAccumulator::reset(InputDevice* device) {
+    mBtnTouch = device->isKeyPressed(BTN_TOUCH);
+    mBtnStylus = device->isKeyPressed(BTN_STYLUS);
+    // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
+    mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
+    mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
+    mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
+    mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
+    mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
+    mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
+    mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
+    mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
+    mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
+    mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
+    mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
+    mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
+}
+
+void TouchButtonAccumulator::clearButtons() {
+    mBtnTouch = 0;
+    mBtnStylus = 0;
+    mBtnStylus2 = 0;
+    mBtnToolFinger = 0;
+    mBtnToolPen = 0;
+    mBtnToolRubber = 0;
+    mBtnToolBrush = 0;
+    mBtnToolPencil = 0;
+    mBtnToolAirbrush = 0;
+    mBtnToolMouse = 0;
+    mBtnToolLens = 0;
+    mBtnToolDoubleTap = 0;
+    mBtnToolTripleTap = 0;
+    mBtnToolQuadTap = 0;
+}
+
+void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+    if (rawEvent->type == EV_KEY) {
+        switch (rawEvent->code) {
+            case BTN_TOUCH:
+                mBtnTouch = rawEvent->value;
+                break;
+            case BTN_STYLUS:
+                mBtnStylus = rawEvent->value;
+                break;
+            case BTN_STYLUS2:
+            case BTN_0: // BTN_0 is what gets mapped for the HID usage
+                        // Digitizers.SecondaryBarrelSwitch
+                mBtnStylus2 = rawEvent->value;
+                break;
+            case BTN_TOOL_FINGER:
+                mBtnToolFinger = rawEvent->value;
+                break;
+            case BTN_TOOL_PEN:
+                mBtnToolPen = rawEvent->value;
+                break;
+            case BTN_TOOL_RUBBER:
+                mBtnToolRubber = rawEvent->value;
+                break;
+            case BTN_TOOL_BRUSH:
+                mBtnToolBrush = rawEvent->value;
+                break;
+            case BTN_TOOL_PENCIL:
+                mBtnToolPencil = rawEvent->value;
+                break;
+            case BTN_TOOL_AIRBRUSH:
+                mBtnToolAirbrush = rawEvent->value;
+                break;
+            case BTN_TOOL_MOUSE:
+                mBtnToolMouse = rawEvent->value;
+                break;
+            case BTN_TOOL_LENS:
+                mBtnToolLens = rawEvent->value;
+                break;
+            case BTN_TOOL_DOUBLETAP:
+                mBtnToolDoubleTap = rawEvent->value;
+                break;
+            case BTN_TOOL_TRIPLETAP:
+                mBtnToolTripleTap = rawEvent->value;
+                break;
+            case BTN_TOOL_QUADTAP:
+                mBtnToolQuadTap = rawEvent->value;
+                break;
+        }
+    }
+}
+
+uint32_t TouchButtonAccumulator::getButtonState() const {
+    uint32_t result = 0;
+    if (mBtnStylus) {
+        result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
+    }
+    if (mBtnStylus2) {
+        result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY;
+    }
+    return result;
+}
+
+int32_t TouchButtonAccumulator::getToolType() const {
+    if (mBtnToolMouse || mBtnToolLens) {
+        return AMOTION_EVENT_TOOL_TYPE_MOUSE;
+    }
+    if (mBtnToolRubber) {
+        return AMOTION_EVENT_TOOL_TYPE_ERASER;
+    }
+    if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
+        return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    }
+    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+        return AMOTION_EVENT_TOOL_TYPE_FINGER;
+    }
+    return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+}
+
+bool TouchButtonAccumulator::isToolActive() const {
+    return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
+            mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
+            mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+}
+
+bool TouchButtonAccumulator::isHovering() const {
+    return mHaveBtnTouch && !mBtnTouch;
+}
+
+bool TouchButtonAccumulator::hasStylus() const {
+    return mHaveStylus;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
new file mode 100644
index 0000000..65b6bdc
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+
+#include <stdint.h>
+
+namespace android {
+
+class InputDevice;
+struct RawEvent;
+
+/* Keeps track of the state of touch, stylus and tool buttons. */
+class TouchButtonAccumulator {
+public:
+    TouchButtonAccumulator();
+    void configure(InputDevice* device);
+    void reset(InputDevice* device);
+
+    void process(const RawEvent* rawEvent);
+
+    uint32_t getButtonState() const;
+    int32_t getToolType() const;
+    bool isToolActive() const;
+    bool isHovering() const;
+    bool hasStylus() const;
+
+private:
+    bool mHaveBtnTouch;
+    bool mHaveStylus;
+
+    bool mBtnTouch;
+    bool mBtnStylus;
+    bool mBtnStylus2;
+    bool mBtnToolFinger;
+    bool mBtnToolPen;
+    bool mBtnToolRubber;
+    bool mBtnToolBrush;
+    bool mBtnToolPencil;
+    bool mBtnToolAirbrush;
+    bool mBtnToolMouse;
+    bool mBtnToolLens;
+    bool mBtnToolDoubleTap;
+    bool mBtnToolTripleTap;
+    bool mBtnToolQuadTap;
+
+    void clearButtons();
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
new file mode 100644
index 0000000..5956fb0
--- /dev/null
+++ b/services/inputflinger/reporter/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+    name: "libinputreporter_headers",
+    export_include_dirs: ["."],
+}
+
+cc_library_shared {
+    name: "libinputreporter",
+    defaults: ["inputflinger_defaults"],
+
+    srcs: [
+        "InputReporter.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+}
+
diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/reporter/InputReporter.cpp
similarity index 93%
rename from services/inputflinger/InputReporter.cpp
rename to services/inputflinger/reporter/InputReporter.cpp
index 8d3153c..b591d3f 100644
--- a/services/inputflinger/InputReporter.cpp
+++ b/services/inputflinger/reporter/InputReporter.cpp
@@ -27,15 +27,15 @@
 };
 
 void InputReporter::reportUnhandledKey(uint32_t sequenceNum) {
-  // do nothing
+    // do nothing
 }
 
 void InputReporter::reportDroppedKey(uint32_t sequenceNum) {
-  // do nothing
+    // do nothing
 }
 
 sp<InputReporterInterface> createInputReporter() {
-  return new InputReporter();
+    return new InputReporter();
 }
 
 } // namespace android
diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h
similarity index 97%
rename from services/inputflinger/include/InputReporterInterface.h
rename to services/inputflinger/reporter/InputReporterInterface.h
index 906d7f2..e5d3606 100644
--- a/services/inputflinger/include/InputReporterInterface.h
+++ b/services/inputflinger/reporter/InputReporterInterface.h
@@ -27,7 +27,7 @@
  */
 class InputReporterInterface : public virtual RefBase {
 protected:
-    virtual ~InputReporterInterface() { }
+    virtual ~InputReporterInterface() {}
 
 public:
     // Report a key that was not handled by the system or apps.
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 9054316..c4f8626 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -32,4 +32,7 @@
         "libinputflinger_base",
         "libinputservice",
     ],
+    header_libs: [
+        "libinputreader_headers",
+    ],
 }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index a86dcbc..aa98ef7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "../InputDispatcher.h"
+#include "../dispatcher/InputDispatcher.h"
+
+#include <InputDispatcherThread.h>
 
 #include <binder/Binder.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
 
-namespace android {
+namespace android::inputdispatcher {
 
 // An arbitrary time value.
 static const nsecs_t ARBITRARY_TIME = 1234;
@@ -1082,4 +1084,4 @@
     mFakePolicy->assertOnPointerDownEquals(nullptr);
 }
 
-} // namespace android
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 348a12b..31b1652 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,8 +14,16 @@
  * limitations under the License.
  */
 
-#include "../InputReader.h"
-#include "TestInputListener.h"
+#include <CursorInputMapper.h>
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <KeyboardInputMapper.h>
+#include <MultiTouchInputMapper.h>
+#include <SingleTouchInputMapper.h>
+#include <SwitchInputMapper.h>
+#include <TestInputListener.h>
+#include <TouchInputMapper.h>
 
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -194,6 +202,20 @@
         mConfig.setDisplayViewports(mViewports);
     }
 
+    bool updateViewport(const DisplayViewport& viewport) {
+        size_t count = mViewports.size();
+        for (size_t i = 0; i < count; i++) {
+            const DisplayViewport& currentViewport = mViewports[i];
+            if (currentViewport.displayId == viewport.displayId) {
+                mViewports[i] = viewport;
+                mConfig.setDisplayViewports(mViewports);
+                return true;
+            }
+        }
+        // no viewport found.
+        return false;
+    }
+
     void addExcludedDeviceName(const std::string& deviceName) {
         mConfig.excludedDeviceNames.push_back(deviceName);
     }
@@ -527,7 +549,7 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
-        if (device) {
+        if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
                 *outAxisInfo = device->absoluteAxes.valueAt(index);
@@ -6554,4 +6576,61 @@
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
+/**
+ * If we had defined port associations, but the viewport is not ready, the touch device would be
+ * expected to be disabled, and it should be enabled after the viewport has found.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    constexpr uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    ASSERT_EQ(mDevice->isEnabled(), false);
+
+    // Add display on hdmi2, the device should be enabled and can receive touch event.
+    prepareSecondaryDisplay(type, hdmi2);
+    ASSERT_EQ(mDevice->isEnabled(), true);
+
+    // Send a touch event.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
+}
+
+/**
+ * Test touch should not work if outside of surface.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    // Let surface be different from physical display.
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    internalViewport->logicalLeft = internalViewport->physicalTop + 20;
+    internalViewport->logicalTop = internalViewport->physicalRight + 20;
+    internalViewport->logicalRight = internalViewport->physicalRight - 20;
+    internalViewport->logicalBottom = internalViewport->physicalBottom - 20;
+    mFakePolicy->updateViewport(internalViewport.value());
+
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    int32_t rawX = 10;
+    int32_t rawY = 10;
+    processPosition(mapper, rawX, rawY);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 } // namespace android
diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp
index 0227164..73802db 100644
--- a/services/schedulerservice/Android.bp
+++ b/services/schedulerservice/Android.bp
@@ -6,8 +6,6 @@
     cflags: ["-Wall", "-Werror"],
     shared_libs: [
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libmediautils",
         "liblog",
         "libutils",
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 33a2747..1c9a4af 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -45,8 +45,6 @@
         "libcrypto",
         "libbase",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libfmq",
         "android.hardware.sensors@1.0",
         "android.hardware.sensors@2.0",
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index 81099e8..90c2330 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1,3 +1,3 @@
 arthuri@google.com
 bduddie@google.com
-bstack@google.com
+stange@google.com
diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp
index 02c13fa..d0c83d6 100644
--- a/services/sensorservice/hidl/Android.bp
+++ b/services/sensorservice/hidl/Android.bp
@@ -13,8 +13,6 @@
     shared_libs: [
         "libbase",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libutils",
         "libsensor",
         "android.frameworks.sensorservice@1.0",
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 93c738d..b404836 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -43,8 +43,6 @@
         "libgui",
         "libhardware",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblayers_proto",
         "liblog",
         "libnativewindow",
@@ -75,7 +73,6 @@
         "libtrace_proto",
         "libvr_manager",
         "libvrflinger",
-        "perfetto_src_tracing_ipc",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -84,7 +81,6 @@
     ],
     export_static_lib_headers: [
         "libcompositionengine",
-        "libperfetto_client_experimental",
         "librenderengine",
         "libserviceutils",
         "libtimestats",
@@ -98,8 +94,6 @@
         "android.hardware.graphics.composer@2.3",
         "android.hardware.power@1.3",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
     ],
 }
 
@@ -220,7 +214,6 @@
         "libcutils",
         "libdisplayservicehidl",
         "libhidlbase",
-        "libhidltransport",
         "libinput",
         "liblayers_proto",
         "liblog",
@@ -268,8 +261,6 @@
         "android.hardware.configstore@1.1",
         "android.hardware.graphics.common@1.2",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libui",
         "libutils",
         "liblog",
@@ -280,8 +271,6 @@
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
     ],
     export_static_lib_headers: [
         "SurfaceFlingerProperties",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 486b042..fba235d 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -22,18 +22,17 @@
 #include "BufferLayer.h"
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerCreationArgs.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <renderengine/RenderEngine.h>
@@ -187,8 +186,7 @@
 
         // Query the texture matrix given our current filtering mode.
         float textureMatrix[16];
-        setFilteringEnabled(useFiltering);
-        memcpy(textureMatrix, mBufferInfo.mTransformMatrix, sizeof(mBufferInfo.mTransformMatrix));
+        getDrawingTransformMatrix(useFiltering, textureMatrix);
 
         if (getTransformToDisplayInverse()) {
             /*
@@ -719,6 +717,11 @@
     return mBufferInfo.mBuffer;
 }
 
+void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
+    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
+                                       mBufferInfo.mTransform, filteringEnabled);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index a685ea2..f0a30e3 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -127,6 +127,10 @@
 
     PixelFormat getPixelFormat() const;
 
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
+
     virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
 
     virtual bool getAutoRefresh() const = 0;
@@ -137,8 +141,6 @@
 
     virtual bool hasFrameUpdate() const = 0;
 
-    virtual void setFilteringEnabled(bool enabled) = 0;
-
     virtual status_t bindTextureImage() = 0;
     virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                     nsecs_t expectedPresentTime) = 0;
@@ -151,9 +153,8 @@
         nsecs_t mDesiredPresentTime;
         std::shared_ptr<FenceTime> mFenceTime;
         sp<Fence> mFence;
-        float mTransformMatrix[16];
         uint32_t mTransform{0};
-        ui::Dataspace mDataspace;
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
         Rect mCrop;
         uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
         Region mSurfaceDamage;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index a9e364a..fcabedf 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,15 +17,13 @@
 #undef LOG_TAG
 #define LOG_TAG "BufferQueueLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <compositionengine/Display.h>
+#include "BufferQueueLayer.h"
+
 #include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueueConsumer.h>
 #include <system/window.h>
 
-#include "BufferQueueLayer.h"
 #include "LayerRejecter.h"
 #include "SurfaceInterceptor.h"
 
@@ -151,13 +149,6 @@
     return mQueueItems[0].mTimestamp <= expectedPresentTime;
 }
 
-// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's
-// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state
-// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's
-// current buffer so the consumer functions start with "getCurrent".
-//
-// This results in the rather confusing functions below.
-
 uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
     uint64_t frameNumber = mQueueItems[0].mFrameNumber;
@@ -203,8 +194,9 @@
     bool sidebandStreamChanged = true;
     if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
         // mSidebandStreamChanged was changed to false
-        auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-        layerCompositionState.sidebandStream = mConsumer->getSidebandStream();
+        mSidebandStream = mConsumer->getSidebandStream();
+        auto& layerCompositionState = getCompositionLayer()->editFEState();
+        layerCompositionState.sidebandStream = mSidebandStream;
         if (layerCompositionState.sidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -220,10 +212,6 @@
     return mQueuedFrames > 0;
 }
 
-void BufferQueueLayer::setFilteringEnabled(bool enabled) {
-    return mConsumer->setFilteringEnabled(enabled);
-}
-
 status_t BufferQueueLayer::bindTextureImage() {
     return mConsumer->bindTextureImage();
 }
@@ -345,7 +333,7 @@
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer =
             mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+    auto& layerCompositionState = getCompositionLayer()->editFEState();
     layerCompositionState.buffer = mBufferInfo.mBuffer;
 
     if (mBufferInfo.mBuffer == nullptr) {
@@ -532,7 +520,6 @@
     mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
     mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
     mBufferInfo.mFence = mConsumer->getCurrentFence();
-    mConsumer->getTransformMatrix(mBufferInfo.mTransformMatrix);
     mBufferInfo.mTransform = mConsumer->getCurrentTransform();
     mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace());
     mBufferInfo.mCrop = mConsumer->getCurrentCrop();
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 43eb3ea..9374741 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -46,7 +46,6 @@
 
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
 
-
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
@@ -65,7 +64,6 @@
     bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
 private:
-
     uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
@@ -75,8 +73,6 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             nsecs_t expectedPresentTime) override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index afbe228..ad05bc8 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -19,18 +19,16 @@
 #define LOG_TAG "BufferStateLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "BufferStateLayer.h"
+
 #include <limits>
 
-#include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueue.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
 
-#include "BufferStateLayer.h"
 #include "ColorLayer.h"
 #include "FrameTracer/FrameTracer.h"
 #include "TimeStats/TimeStats.h"
@@ -415,7 +413,7 @@
         // mSidebandStreamChanged was true
         LOG_ALWAYS_FATAL_IF(!getCompositionLayer());
         mSidebandStream = s.sidebandStream;
-        getCompositionLayer()->editState().frontEnd.sidebandStream = mSidebandStream;
+        getCompositionLayer()->editFEState().sidebandStream = mSidebandStream;
         if (mSidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -432,11 +430,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-void BufferStateLayer::setFilteringEnabled(bool enabled) {
-    GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mBufferInfo.mBuffer,
-                                       mBufferInfo.mCrop, mBufferInfo.mTransform, enabled);
-}
-
 status_t BufferStateLayer::bindTextureImage() {
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
@@ -525,7 +518,7 @@
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer = s.buffer;
     mBufferInfo.mFence = s.acquireFence;
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+    auto& layerCompositionState = getCompositionLayer()->editFEState();
     layerCompositionState.buffer = mBufferInfo.mBuffer;
 
     return NO_ERROR;
@@ -545,10 +538,8 @@
         return;
     }
 
-    const State& s(getDrawingState());
-
-    compositionState.buffer = s.buffer;
-    compositionState.bufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
+    compositionState.buffer = mBufferInfo.mBuffer;
+    compositionState.bufferSlot = mBufferInfo.mBufferSlot;
     compositionState.acquireFence = mBufferInfo.mFence;
 
     mFrameNumber++;
@@ -641,8 +632,6 @@
     mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
-    std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix),
-              mBufferInfo.mTransformMatrix);
     mBufferInfo.mTransform = s.transform;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
     mBufferInfo.mCrop = computeCrop(s);
@@ -653,6 +642,7 @@
     mBufferInfo.mPixelFormat =
             !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
     mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
+    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
 }
 
 Rect BufferStateLayer::computeCrop(const State& s) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0b27d64..52063fc 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -19,7 +19,6 @@
 #include "BufferLayer.h"
 #include "Layer.h"
 
-#include <gui/GLConsumer.h>
 #include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
@@ -108,7 +107,6 @@
     void gatherBufferInfo() override;
 
 private:
-
     uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
@@ -118,8 +116,6 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             nsecs_t expectedPresentTime) override;
@@ -141,8 +137,6 @@
 
     std::unique_ptr<renderengine::Image> mTextureImage;
 
-    std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX};
-
     std::atomic<bool> mSidebandStreamChanged{false};
 
     mutable uint32_t mFrameNumber{0};
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 49b1810..99805d9 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -25,13 +25,9 @@
 #include <sys/types.h>
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerCreationArgs.h>
 #include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <renderengine/RenderEngine.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Errors.h>
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index e49b65f..fcb94fd 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -47,7 +47,7 @@
         "src/DumpHelpers.cpp",
         "src/HwcBufferCache.cpp",
         "src/Layer.cpp",
-        "src/LayerCompositionState.cpp",
+        "src/LayerFECompositionState.cpp",
         "src/Output.cpp",
         "src/OutputCompositionState.cpp",
         "src/OutputLayer.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 6f689ad..90158c7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -18,6 +18,7 @@
 
 #include <chrono>
 #include <optional>
+#include <vector>
 
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
@@ -28,6 +29,7 @@
 
 using Layers = std::vector<std::shared_ptr<compositionengine::Layer>>;
 using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+using RawLayers = std::vector<compositionengine::Layer*>;
 
 /**
  * A parameter object for refreshing a set of outputs
@@ -41,6 +43,9 @@
     // front.
     Layers layers;
 
+    // All the layers that have queued updates.
+    RawLayers layersWithQueuedFrames;
+
     // If true, forces the entire display to be considered dirty and repainted
     bool repaintEverything{false};
 
@@ -53,6 +58,9 @@
     // Forces a color mode on the outputs being refreshed
     ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
 
+    // If true, the complete output geometry needs to be recomputed this frame
+    bool updatingOutputGeometryThisFrame{false};
+
     // If true, there was a geometry update this frame
     bool updatingGeometryThisFrame{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
index 451608b..1259c52 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
@@ -26,9 +26,7 @@
 class Display;
 class LayerFE;
 
-namespace impl {
-struct LayerCompositionState;
-} // namespace impl
+struct LayerFECompositionState;
 
 /**
  * A layer contains the output-independent composition state for a front-end
@@ -42,17 +40,13 @@
     // front-end layer no longer exists.
     virtual sp<LayerFE> getLayerFE() const = 0;
 
-    using CompositionState = impl::LayerCompositionState;
-
-    // Gets the raw composition state data for the layer
+    // Gets the raw front-end composition state data for the layer
     // TODO(lpique): Make this protected once it is only internally called.
-    virtual const CompositionState& getState() const = 0;
+    virtual const LayerFECompositionState& getFEState() const = 0;
 
-    // Allows mutable access to the raw composition state data for the layer.
-    // This is meant to be used by the various functions that are part of the
-    // composition process.
+    // Allows mutable access to the raw front-end composition state
     // TODO(lpique): Make this protected once it is only internally called.
-    virtual CompositionState& editState() = 0;
+    virtual LayerFECompositionState& editFEState() = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 57cca69..e585769 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <optional>
+#include <unordered_set>
 
 #include <renderengine/LayerSettings.h>
 #include <utils/RefBase.h>
@@ -99,5 +100,13 @@
     virtual const char* getDebugName() const = 0;
 };
 
+// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
+// be removed.
+struct LayerFESpHash {
+    size_t operator()(const sp<LayerFE>& p) const { return std::hash<LayerFE*>()(p.get()); }
+};
+
+using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+
 } // namespace compositionengine
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 530f49a..2ba781d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -147,6 +147,9 @@
 
     // The output-independent frame for the cursor
     Rect cursorFrame;
+
+    // Debugging
+    void dump(std::string& out) const;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2e44c07..d374baa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <unordered_map>
 
+#include <compositionengine/LayerFE.h>
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
@@ -71,6 +72,22 @@
         ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
     };
 
+    // Use internally to incrementally compute visibility/coverage
+    struct CoverageState {
+        explicit CoverageState(LayerFESet& latchedLayers) : latchedLayers(latchedLayers) {}
+
+        // The set of layers that had been latched for the coverage calls, to
+        // avoid duplicate requests to obtain the same front-end layer state.
+        LayerFESet& latchedLayers;
+
+        // The region of the output which is covered by layers
+        Region aboveCoveredLayers;
+        // The region of the output which is opaquely covered by layers
+        Region aboveOpaqueLayers;
+        // The region of the output which should be considered dirty
+        Region dirtyRegion;
+    };
+
     virtual ~Output();
 
     // Returns true if the output is valid. This is meant to be checked post-
@@ -134,6 +151,9 @@
     // this output allows that.
     virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
 
+    // Determines if a layer belongs to the output.
+    virtual bool belongsInOutput(const compositionengine::Layer*) const = 0;
+
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
     virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
@@ -161,7 +181,7 @@
     virtual ReleasedLayers takeReleasedLayers() = 0;
 
     // Prepare the output, updating the OutputLayers used in the output
-    virtual void prepare(CompositionRefreshArgs&) = 0;
+    virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
 
     // Presents the output, finalizing all composition details
     virtual void present(const CompositionRefreshArgs&) = 0;
@@ -173,6 +193,13 @@
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
 
+    virtual void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&,
+                                    LayerFESet&) = 0;
+    virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
+    virtual std::unique_ptr<OutputLayer> getOutputLayerIfVisible(
+            std::shared_ptr<compositionengine::Layer>, CoverageState&) = 0;
+    virtual void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) = 0;
+
     virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
     virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
     virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 2e595d6..b5d8325 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -42,6 +42,8 @@
     void dump(std::string&) const override;
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
             const std::shared_ptr<Layer>&, const sp<LayerFE>&) const override;
+    using compositionengine::impl::Output::setReleasedLayers;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
     void chooseCompositionStrategy() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
index 3e56b21..d441c9c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
@@ -19,7 +19,7 @@
 #include <memory>
 
 #include <compositionengine/Layer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
@@ -41,8 +41,8 @@
 
     sp<LayerFE> getLayerFE() const override;
 
-    const LayerCompositionState& getState() const override;
-    LayerCompositionState& editState() override;
+    const LayerFECompositionState& getFEState() const override;
+    LayerFECompositionState& editFEState() override;
 
     void dump(std::string& result) const override;
 
@@ -50,7 +50,8 @@
     const compositionengine::CompositionEngine& mCompositionEngine;
     const wp<LayerFE> mLayerFE;
 
-    LayerCompositionState mState;
+    // State obtained from calls to LayerFE::getCompositionState
+    LayerFECompositionState mFrontEndState;
 };
 
 std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
deleted file mode 100644
index 726c850..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/Mesh.h>
-#include <ui/Region.h>
-
-namespace android {
-
-namespace compositionengine::impl {
-
-struct LayerCompositionState {
-    /*
-     * State set by LayerFE::getCompositionState
-     */
-
-    LayerFECompositionState frontEnd;
-
-    // Debugging
-    void dump(std::string& result) const;
-};
-
-} // namespace compositionengine::impl
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index bdb35c2..fa6cd27 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -63,6 +63,7 @@
 
     Region getDirtyRegion(bool repaintEverything) const override;
     bool belongsInOutput(std::optional<uint32_t>, bool) const override;
+    bool belongsInOutput(const compositionengine::Layer*) const override;
 
     compositionengine::OutputLayer* getOutputLayerForLayer(
             compositionengine::Layer*) const override;
@@ -76,9 +77,17 @@
     void setReleasedLayers(ReleasedLayers&&) override;
     ReleasedLayers takeReleasedLayers() override;
 
-    void prepare(compositionengine::CompositionRefreshArgs&) override;
+    void prepare(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
     void present(const compositionengine::CompositionRefreshArgs&) override;
 
+    void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
+    void collectVisibleLayers(const compositionengine::CompositionRefreshArgs&,
+                              compositionengine::Output::CoverageState&) override;
+    std::unique_ptr<compositionengine::OutputLayer> getOutputLayerIfVisible(
+            std::shared_ptr<compositionengine::Layer>,
+            compositionengine::Output::CoverageState&) override;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+
     void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
     void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -90,11 +99,14 @@
     void postFramebuffer() override;
 
     // Testing
+    const ReleasedLayers& getReleasedLayersForTest() const;
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
 
 protected:
     const CompositionEngine& getCompositionEngine() const;
+    std::unique_ptr<compositionengine::OutputLayer> takeOutputLayerForLayer(
+            compositionengine::Layer*);
     void chooseCompositionStrategy() override;
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
index cce3b97..4f03cb4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
@@ -18,7 +18,7 @@
 
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
-#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gmock/gmock.h>
 
 namespace android::compositionengine::mock {
@@ -30,8 +30,8 @@
 
     MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
 
-    MOCK_CONST_METHOD0(getState, const CompositionState&());
-    MOCK_METHOD0(editState, CompositionState&());
+    MOCK_CONST_METHOD0(getFEState, const LayerFECompositionState&());
+    MOCK_METHOD0(editFEState, LayerFECompositionState&());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 93274e7..286a20f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -60,6 +60,7 @@
 
     MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
     MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
+    MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
                        compositionengine::OutputLayer*(compositionengine::Layer*));
@@ -78,9 +79,20 @@
     MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
     MOCK_METHOD0(takeReleasedLayers, ReleasedLayers());
 
-    MOCK_METHOD1(prepare, void(compositionengine::CompositionRefreshArgs&));
+    MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
     MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
 
+    MOCK_METHOD2(rebuildLayerStacks,
+                 void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD2(collectVisibleLayers,
+                 void(const compositionengine::CompositionRefreshArgs&,
+                      compositionengine::Output::CoverageState&));
+    MOCK_METHOD2(getOutputLayerIfVisible,
+                 std::unique_ptr<compositionengine::OutputLayer>(
+                         std::shared_ptr<compositionengine::Layer>,
+                         compositionengine::Output::CoverageState&));
+    MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+
     MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 590c596..8391458 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -72,8 +72,20 @@
 }
 
 void CompositionEngine::present(CompositionRefreshArgs& args) {
-    for (const auto& output : args.outputs) {
-        output->prepare(args);
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    preComposition(args);
+
+    {
+        // latchedLayers is used to track the set of front-end layer state that
+        // has been latched across all outputs for the prepare step, and is not
+        // needed for anything else.
+        LayerFESet latchedLayers;
+
+        for (const auto& output : args.outputs) {
+            output->prepare(args, latchedLayers);
+        }
     }
 
     updateLayerStateFromFE(args);
@@ -91,8 +103,8 @@
         for (auto& layer : output->getOutputLayersOrderedByZ()) {
             if (layer->isHardwareCursor()) {
                 // Latch the cursor composition state from each front-end layer.
-                layer->getLayerFE().latchCursorCompositionState(
-                        layer->getLayer().editState().frontEnd);
+                layer->getLayerFE().latchCursorCompositionState(layer->getLayer().editFEState());
+
                 layer->writeCursorPositionToHWC();
             }
         }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 49727df..fe8fa21 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -158,6 +158,39 @@
     return result;
 }
 
+void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    Output::setReleasedLayers(refreshArgs);
+
+    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+        return;
+    }
+
+    // For layers that are being removed from a HWC display, and that have
+    // queued frames, add them to a a list of released layers so we can properly
+    // set a fence.
+    compositionengine::Output::ReleasedLayers releasedLayers;
+
+    // Any non-null entries in the current list of layers are layers that are no
+    // longer going to be visible
+    for (auto& layer : getOutputLayersOrderedByZ()) {
+        if (!layer) {
+            continue;
+        }
+
+        sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE());
+        const bool hasQueuedFrames =
+                std::find(refreshArgs.layersWithQueuedFrames.cbegin(),
+                          refreshArgs.layersWithQueuedFrames.cend(),
+                          &layer->getLayer()) != refreshArgs.layersWithQueuedFrames.cend();
+
+        if (hasQueuedFrames) {
+            releasedLayers.emplace_back(layerFE);
+        }
+    }
+
+    setReleasedLayers(std::move(releasedLayers));
+}
+
 void Display::chooseCompositionStrategy() {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
index 96e9731..8a1cbe4 100644
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -43,19 +43,21 @@
     return mLayerFE.promote();
 }
 
-const LayerCompositionState& Layer::getState() const {
-    return mState;
+const compositionengine::LayerFECompositionState& Layer::getFEState() const {
+    return mFrontEndState;
 }
 
-LayerCompositionState& Layer::editState() {
-    return mState;
+compositionengine::LayerFECompositionState& Layer::editFEState() {
+    return mFrontEndState;
 }
 
 void Layer::dump(std::string& out) const {
     auto layerFE = getLayerFE();
     android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
                                  layerFE ? layerFE->getDebugName() : "<unknown>");
-    mState.dump(out);
+
+    out.append("    frontend:\n");
+    mFrontEndState.dump(out);
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
deleted file mode 100644
index c5debf6..0000000
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/stringprintf.h>
-#include <compositionengine/impl/DumpHelpers.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-
-namespace android::compositionengine::impl {
-
-namespace {
-
-using android::compositionengine::impl::dumpVal;
-
-void dumpVal(std::string& out, const char* name, half4 value) {
-    using android::base::StringAppendF;
-    StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r),
-                  static_cast<float>(value.g), static_cast<float>(value.b));
-}
-
-void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
-    out.append("      ");
-    dumpVal(out, "isSecure", state.isSecure);
-    dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop);
-    dumpVal(out, "geomBufferUsesDisplayInverseTransform",
-            state.geomBufferUsesDisplayInverseTransform);
-    dumpVal(out, "geomLayerTransform", state.geomLayerTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "geomBufferSize", state.geomBufferSize);
-    dumpVal(out, "geomContentCrop", state.geomContentCrop);
-    dumpVal(out, "geomCrop", state.geomCrop);
-    dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "transparentRegionHint", state.transparentRegionHint);
-
-    out.append("      ");
-    dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
-
-    out.append("\n      ");
-    dumpVal(out, "blend", toString(state.blendMode), state.blendMode);
-    dumpVal(out, "alpha", state.alpha);
-
-    out.append("\n      ");
-    dumpVal(out, "type", state.type);
-    dumpVal(out, "appId", state.appId);
-
-    dumpVal(out, "composition type", toString(state.compositionType), state.compositionType);
-
-    out.append("\n      buffer: ");
-    dumpVal(out, "bufferSlot", state.bufferSlot);
-    dumpVal(out, "buffer", state.buffer.get());
-
-    out.append("\n      ");
-    dumpVal(out, "sideband stream", state.sidebandStream.get());
-
-    out.append("\n      ");
-    dumpVal(out, "color", state.color);
-
-    out.append("\n      ");
-    dumpVal(out, "isOpaque", state.isOpaque);
-    dumpVal(out, "hasProtectedContent", state.hasProtectedContent);
-    dumpVal(out, "isColorspaceAgnostic", state.isColorspaceAgnostic);
-    dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace);
-    dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes);
-    dumpVal(out, "colorTransform", state.colorTransform);
-
-    out.append("\n");
-}
-
-} // namespace
-
-void LayerCompositionState::dump(std::string& out) const {
-    out.append("    frontend:\n");
-    dumpFrontEnd(out, frontEnd);
-}
-
-} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
new file mode 100644
index 0000000..1ca03dc
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine {
+
+namespace {
+
+using android::compositionengine::impl::dumpVal;
+
+void dumpVal(std::string& out, const char* name, half4 value) {
+    using android::base::StringAppendF;
+    StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r),
+                  static_cast<float>(value.g), static_cast<float>(value.b));
+}
+
+} // namespace
+
+void LayerFECompositionState::dump(std::string& out) const {
+    out.append("      ");
+    dumpVal(out, "isSecure", isSecure);
+    dumpVal(out, "geomUsesSourceCrop", geomUsesSourceCrop);
+    dumpVal(out, "geomBufferUsesDisplayInverseTransform", geomBufferUsesDisplayInverseTransform);
+    dumpVal(out, "geomLayerTransform", geomLayerTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "geomBufferSize", geomBufferSize);
+    dumpVal(out, "geomContentCrop", geomContentCrop);
+    dumpVal(out, "geomCrop", geomCrop);
+    dumpVal(out, "geomBufferTransform", geomBufferTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "transparentRegionHint", transparentRegionHint);
+
+    out.append("      ");
+    dumpVal(out, "geomLayerBounds", geomLayerBounds);
+
+    out.append("\n      ");
+    dumpVal(out, "blend", toString(blendMode), blendMode);
+    dumpVal(out, "alpha", alpha);
+
+    out.append("\n      ");
+    dumpVal(out, "type", type);
+    dumpVal(out, "appId", appId);
+
+    dumpVal(out, "composition type", toString(compositionType), compositionType);
+
+    out.append("\n      buffer: ");
+    dumpVal(out, "slot", bufferSlot);
+    dumpVal(out, "buffer", buffer.get());
+
+    out.append("\n      ");
+    dumpVal(out, "sideband stream", sidebandStream.get());
+
+    out.append("\n      ");
+    dumpVal(out, "color", color);
+
+    out.append("\n      ");
+    dumpVal(out, "isOpaque", isOpaque);
+    dumpVal(out, "hasProtectedContent", hasProtectedContent);
+    dumpVal(out, "isColorspaceAgnostic", isColorspaceAgnostic);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index cb04e95..02ebc1f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -22,8 +22,8 @@
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/RenderSurface.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/Output.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <renderengine/DisplaySettings.h>
@@ -40,6 +40,27 @@
 
 namespace impl {
 
+namespace {
+
+template <typename T>
+class Reversed {
+public:
+    explicit Reversed(const T& container) : mContainer(container) {}
+    auto begin() { return mContainer.rbegin(); }
+    auto end() { return mContainer.rend(); }
+
+private:
+    const T& mContainer;
+};
+
+// Helper for enumerating over a container in reverse order
+template <typename T>
+Reversed<T> reversed(const T& c) {
+    return Reversed<T>(c);
+}
+
+} // namespace
+
 Output::Output(const CompositionEngine& compositionEngine)
       : mCompositionEngine(compositionEngine) {}
 
@@ -176,6 +197,10 @@
     mDisplayColorProfile = std::move(mode);
 }
 
+const Output::ReleasedLayers& Output::getReleasedLayersForTest() const {
+    return mReleasedLayers;
+}
+
 void Output::setDisplayColorProfileForTest(
         std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
     mDisplayColorProfile = std::move(mode);
@@ -219,6 +244,15 @@
             (!internalOnly || mState.layerStackInternal);
 }
 
+bool Output::belongsInOutput(const compositionengine::Layer* layer) const {
+    if (!layer) {
+        return false;
+    }
+
+    const auto& layerFEState = layer->getFEState();
+    return belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly);
+}
+
 compositionengine::OutputLayer* Output::getOutputLayerForLayer(
         compositionengine::Layer* layer) const {
     for (const auto& outputLayer : mOutputLayersOrderedByZ) {
@@ -229,15 +263,24 @@
     return nullptr;
 }
 
-std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
-        std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
+std::unique_ptr<compositionengine::OutputLayer> Output::takeOutputLayerForLayer(
+        compositionengine::Layer* layer) {
+    // Removes the outputLayer from mOutputLayersorderedByZ and transfers ownership to the caller.
     for (auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer.get()) {
+        if (outputLayer && &outputLayer->getLayer() == layer) {
             return std::move(outputLayer);
         }
     }
+    return nullptr;
+}
 
-    return createOutputLayer(layer, layerFE);
+std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
+        std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
+    auto result = takeOutputLayerForLayer(layer.get());
+    if (!result) {
+        result = createOutputLayer(layer, layerFE);
+    }
+    return result;
 }
 
 std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
@@ -262,19 +305,18 @@
     return std::move(mReleasedLayers);
 }
 
-void Output::prepare(compositionengine::CompositionRefreshArgs& refreshArgs) {
-    if (CC_LIKELY(!refreshArgs.updatingGeometryThisFrame)) {
-        return;
-    }
+void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                     LayerFESet& geomSnapshots) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
 
-    uint32_t zOrder = 0;
-    for (auto& layer : mOutputLayersOrderedByZ) {
-        // Assign a simple Z order sequence to each visible layer.
-        layer->editState().z = zOrder++;
-    }
+    rebuildLayerStacks(refreshArgs, geomSnapshots);
 }
 
 void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
     updateColorProfile(refreshArgs);
     updateAndWriteCompositionState(refreshArgs);
     setColorTransform(refreshArgs);
@@ -285,9 +327,252 @@
     postFramebuffer();
 }
 
+void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                LayerFESet& layerFESet) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    // Do nothing if this output is not enabled or there is no need to perform this update
+    if (!mState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
+        return;
+    }
+
+    // Process the layers to determine visibility and coverage
+    compositionengine::Output::CoverageState coverage{layerFESet};
+    collectVisibleLayers(refreshArgs, coverage);
+
+    // Compute the resulting coverage for this output, and store it for later
+    const ui::Transform& tr = mState.transform;
+    Region undefinedRegion{mState.bounds};
+    undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
+
+    mState.undefinedRegion = undefinedRegion;
+    mState.dirtyRegion.orSelf(coverage.dirtyRegion);
+}
+
+void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                  compositionengine::Output::CoverageState& coverage) {
+    // We build up a list of all layers that are going to be visible in the new
+    // frame.
+    compositionengine::Output::OutputLayers newLayersSortedByZ;
+
+    // Evaluate the layers from front to back to determine what is visible. This
+    // also incrementally calculates the coverage information for each layer as
+    // well as the entire output.
+    for (auto& layer : reversed(refreshArgs.layers)) {
+        // Incrementally process the coverage for each layer, obtaining an
+        // optional outputLayer if the layer is visible.
+        auto outputLayer = getOutputLayerIfVisible(layer, coverage);
+        if (outputLayer) {
+            newLayersSortedByZ.emplace_back(std::move(outputLayer));
+        }
+
+        // TODO(b/121291683): Stop early if the output is completely covered and
+        // no more layers could even be visible underneath the ones on top.
+    }
+
+    // Since we walked the layers in reverse order, we need to reverse
+    // newLayersSortedByZ to get the back-to-front ordered list of layers.
+    std::reverse(newLayersSortedByZ.begin(), newLayersSortedByZ.end());
+
+    // Generate a simple Z-order values to each visible output layer
+    uint32_t zOrder = 0;
+    for (auto& outputLayer : newLayersSortedByZ) {
+        outputLayer->editState().z = zOrder++;
+    }
+
+    setReleasedLayers(refreshArgs);
+
+    mOutputLayersOrderedByZ = std::move(newLayersSortedByZ);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::getOutputLayerIfVisible(
+        std::shared_ptr<compositionengine::Layer> layer,
+        compositionengine::Output::CoverageState& coverage) {
+    // Note: Converts a wp<LayerFE> to a sp<LayerFE>
+    auto layerFE = layer->getLayerFE();
+    if (layerFE == nullptr) {
+        return nullptr;
+    }
+
+    // Ensure we have a snapshot of the basic geometry layer state. Limit the
+    // snapshots to once per frame for each candidate layer, as layers may
+    // appear on multiple outputs.
+    if (!coverage.latchedLayers.count(layerFE)) {
+        coverage.latchedLayers.insert(layerFE);
+        layerFE->latchCompositionState(layer->editFEState(),
+                                       compositionengine::LayerFE::StateSubset::BasicGeometry);
+    }
+
+    // Obtain a read-only reference to the front-end layer state
+    const auto& layerFEState = layer->getFEState();
+
+    // Only consider the layers on the given layer stack
+    if (!belongsInOutput(layer.get())) {
+        return nullptr;
+    }
+
+    /*
+     * opaqueRegion: area of a surface that is fully opaque.
+     */
+    Region opaqueRegion;
+
+    /*
+     * visibleRegion: area of a surface that is visible on screen and not fully
+     * transparent. This is essentially the layer's footprint minus the opaque
+     * regions above it. Areas covered by a translucent surface are considered
+     * visible.
+     */
+    Region visibleRegion;
+
+    /*
+     * coveredRegion: area of a surface that is covered by all visible regions
+     * above it (which includes the translucent areas).
+     */
+    Region coveredRegion;
+
+    /*
+     * transparentRegion: area of a surface that is hinted to be completely
+     * transparent. This is only used to tell when the layer has no visible non-
+     * transparent regions and can be removed from the layer list. It does not
+     * affect the visibleRegion of this layer or any layers beneath it. The hint
+     * may not be correct if apps don't respect the SurfaceView restrictions
+     * (which, sadly, some don't).
+     */
+    Region transparentRegion;
+
+    // handle hidden surfaces by setting the visible region to empty
+    if (CC_UNLIKELY(!layerFEState.isVisible)) {
+        return nullptr;
+    }
+
+    const ui::Transform& tr = layerFEState.geomLayerTransform;
+
+    // Get the visible region
+    // TODO(b/121291683): Is it worth creating helper methods on LayerFEState
+    // for computations like this?
+    visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
+
+    if (visibleRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // Remove the transparent area from the visible region
+    if (!layerFEState.isOpaque) {
+        if (tr.preserveRects()) {
+            // transform the transparent region
+            transparentRegion = tr.transform(layerFEState.transparentRegionHint);
+        } else {
+            // transformation too complex, can't do the
+            // transparent region optimization.
+            transparentRegion.clear();
+        }
+    }
+
+    // compute the opaque region
+    const int32_t layerOrientation = tr.getOrientation();
+    if (layerFEState.isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
+        // If we one of the simple category of transforms (0/90/180/270 rotation
+        // + any flip), then the opaque region is the layer's footprint.
+        // Otherwise we don't try and compute the opaque region since there may
+        // be errors at the edges, and we treat the entire layer as
+        // translucent.
+        opaqueRegion = visibleRegion;
+    }
+
+    // Clip the covered region to the visible region
+    coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
+
+    // Update accumAboveCoveredLayers for next (lower) layer
+    coverage.aboveCoveredLayers.orSelf(visibleRegion);
+
+    // subtract the opaque region covered by the layers above us
+    visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
+
+    if (visibleRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // Get coverage information for the layer as previously displayed,
+    // also taking over ownership from mOutputLayersorderedByZ.
+    auto prevOutputLayer = takeOutputLayerForLayer(layer.get());
+
+    //  Get coverage information for the layer as previously displayed
+    // TODO(b/121291683): Define kEmptyRegion as a constant in Region.h
+    const Region kEmptyRegion;
+    const Region& oldVisibleRegion =
+            prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
+    const Region& oldCoveredRegion =
+            prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
+
+    // compute this layer's dirty region
+    Region dirty;
+    if (layerFEState.contentDirty) {
+        // we need to invalidate the whole region
+        dirty = visibleRegion;
+        // as well, as the old visible region
+        dirty.orSelf(oldVisibleRegion);
+    } else {
+        /* compute the exposed region:
+         *   the exposed region consists of two components:
+         *   1) what's VISIBLE now and was COVERED before
+         *   2) what's EXPOSED now less what was EXPOSED before
+         *
+         * note that (1) is conservative, we start with the whole visible region
+         * but only keep what used to be covered by something -- which mean it
+         * may have been exposed.
+         *
+         * (2) handles areas that were not covered by anything but got exposed
+         * because of a resize.
+         *
+         */
+        const Region newExposed = visibleRegion - coveredRegion;
+        const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+        dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
+    }
+    dirty.subtractSelf(coverage.aboveOpaqueLayers);
+
+    // accumulate to the screen dirty region
+    coverage.dirtyRegion.orSelf(dirty);
+
+    // Update accumAboveOpaqueLayers for next (lower) layer
+    coverage.aboveOpaqueLayers.orSelf(opaqueRegion);
+
+    // Compute the visible non-transparent region
+    Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
+
+    // Peform the final check to see if this layer is visible on this output
+    // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
+    Region drawRegion(mState.transform.transform(visibleNonTransparentRegion));
+    drawRegion.andSelf(mState.bounds);
+    if (drawRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // The layer is visible. Either reuse the existing outputLayer if we have
+    // one, or create a new one if we do not.
+    std::unique_ptr<compositionengine::OutputLayer> result =
+            prevOutputLayer ? std::move(prevOutputLayer) : createOutputLayer(layer, layerFE);
+
+    // Store the layer coverage information into the layer state as some of it
+    // is useful later.
+    auto& outputLayerState = result->editState();
+    outputLayerState.visibleRegion = visibleRegion;
+    outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
+    outputLayerState.coveredRegion = coveredRegion;
+    outputLayerState.outputSpaceVisibleRegion =
+            mState.transform.transform(outputLayerState.visibleRegion.intersect(mState.viewport));
+
+    return result;
+}
+
+void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
+    // The base class does nothing with this call.
+}
+
 void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
     for (auto& layer : mOutputLayersOrderedByZ) {
-        layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
+        layer->getLayerFE().latchCompositionState(layer->getLayer().editFEState(),
                                                   args.updatingGeometryThisFrame
                                                           ? LayerFE::StateSubset::GeometryAndContent
                                                           : LayerFE::StateSubset::Content);
@@ -330,7 +615,7 @@
     *outHdrDataSpace = ui::Dataspace::UNKNOWN;
 
     for (const auto& layer : mOutputLayersOrderedByZ) {
-        switch (layer->getLayer().getState().frontEnd.dataspace) {
+        switch (layer->getLayer().getFEState().dataspace) {
             case ui::Dataspace::V0_SCRGB:
             case ui::Dataspace::V0_SCRGB_LINEAR:
             case ui::Dataspace::BT2020:
@@ -346,8 +631,7 @@
             case ui::Dataspace::BT2020_ITU_PQ:
                 bestDataSpace = ui::Dataspace::DISPLAY_P3;
                 *outHdrDataSpace = ui::Dataspace::BT2020_PQ;
-                *outIsHdrClientComposition =
-                        layer->getLayer().getState().frontEnd.forceClientComposition;
+                *outIsHdrClientComposition = layer->getLayer().getFEState().forceClientComposition;
                 break;
             case ui::Dataspace::BT2020_HLG:
             case ui::Dataspace::BT2020_ITU_HLG:
@@ -553,7 +837,7 @@
         bool needsProtected =
                 std::any_of(mOutputLayersOrderedByZ.begin(), mOutputLayersOrderedByZ.end(),
                             [](auto& layer) {
-                                return layer->getLayer().getState().frontEnd.hasProtectedContent;
+                                return layer->getLayer().getFEState().hasProtectedContent;
                             });
         if (needsProtected != renderEngine.isProtected()) {
             renderEngine.useProtectedContext(needsProtected);
@@ -607,7 +891,7 @@
 
     for (auto& layer : mOutputLayersOrderedByZ) {
         const auto& layerState = layer->getState();
-        const auto& layerFEState = layer->getLayer().getState().frontEnd;
+        const auto& layerFEState = layer->getLayer().getFEState();
         auto& layerFE = layer->getLayerFE();
 
         const Region clip(viewportRegion.intersect(layerState.visibleRegion));
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 4eb256f..43ab87a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -18,8 +18,8 @@
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -86,7 +86,7 @@
 }
 
 Rect OutputLayer::calculateInitialCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = mLayer->getFEState();
 
     // apply the projection's clipping to the window crop in
     // layerstack space, and convert-back to layer space.
@@ -119,7 +119,7 @@
 }
 
 FloatRect OutputLayer::calculateOutputSourceCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = mLayer->getFEState();
     const auto& outputState = mOutput.getState();
 
     if (!layerState.geomUsesSourceCrop) {
@@ -196,7 +196,7 @@
 }
 
 Rect OutputLayer::calculateOutputDisplayFrame() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = mLayer->getFEState();
     const auto& outputState = mOutput.getState();
 
     // apply the layer's transform, followed by the display's global transform
@@ -243,7 +243,7 @@
 }
 
 uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = mLayer->getFEState();
     const auto& outputState = mOutput.getState();
 
     /*
@@ -283,11 +283,18 @@
 } // namespace impl
 
 void OutputLayer::updateCompositionState(bool includeGeometry) {
-    const auto& layerFEState = mLayer->getState().frontEnd;
+    const auto& layerFEState = mLayer->getFEState();
     const auto& outputState = mOutput.getState();
     const auto& profile = *mOutput.getDisplayColorProfile();
 
     if (includeGeometry) {
+        // Clear the forceClientComposition flag before it is set for any
+        // reason. Note that since it can be set by some checks below when
+        // updating the geometry state, we only clear it when updating the
+        // geometry since those conditions for forcing client composition won't
+        // go away otherwise.
+        mState.forceClientComposition = false;
+
         mState.displayFrame = calculateOutputDisplayFrame();
         mState.sourceCrop = calculateOutputSourceCrop();
         mState.bufferTransform =
@@ -327,7 +334,7 @@
         return;
     }
 
-    const auto& outputIndependentState = mLayer->getState().frontEnd;
+    const auto& outputIndependentState = mLayer->getFEState();
     auto requestedCompositionType = outputIndependentState.compositionType;
 
     if (includeGeometry) {
@@ -544,7 +551,7 @@
         return;
     }
 
-    const auto& layerFEState = mLayer->getState().frontEnd;
+    const auto& layerFEState = mLayer->getFEState();
     const auto& outputState = mOutput.getState();
 
     Rect frame = layerFEState.cursorFrame;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 6821ec1..74b3ada 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -277,6 +277,79 @@
 }
 
 /*
+ * Display::setReleasedLayers()
+ */
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    mock::Layer layerXLayer;
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        nonHwcDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
+
+    nonHwcDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) {
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        mDisplay.setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    mDisplay.setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayers) {
+    sp<mock::LayerFE> layer1LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layer2LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layer3LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    mock::Layer layer1Layer;
+    mock::Layer layer2Layer;
+    mock::Layer layer3Layer;
+    mock::Layer layerXLayer;
+
+    EXPECT_CALL(*mLayer1, getLayer()).WillRepeatedly(ReturnRef(layer1Layer));
+    EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(ReturnRef(*layer1LayerFE.get()));
+    EXPECT_CALL(*mLayer2, getLayer()).WillRepeatedly(ReturnRef(layer2Layer));
+    EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(ReturnRef(*layer2LayerFE.get()));
+    EXPECT_CALL(*mLayer3, getLayer()).WillRepeatedly(ReturnRef(layer3Layer));
+    EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(ReturnRef(*layer3LayerFE.get()));
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(&layer1Layer);
+    refreshArgs.layersWithQueuedFrames.push_back(&layer2Layer);
+    refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
+    refreshArgs.layersWithQueuedFrames.push_back(nullptr);
+
+    mDisplay.setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+    ASSERT_EQ(2, releasedLayers.size());
+    ASSERT_EQ(layer1LayerFE.get(), releasedLayers[0].promote().get());
+    ASSERT_EQ(layer2LayerFE.get(), releasedLayers[1].promote().get());
+}
+
+/*
  * Display::chooseCompositionStrategy()
  */
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index b73c47b..a1c156a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -59,7 +59,7 @@
         EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
         EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
 
-        EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+        EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
         EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
     }
 
@@ -70,7 +70,7 @@
             new StrictMock<compositionengine::mock::LayerFE>()};
     impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
 
-    impl::LayerCompositionState mLayerState;
+    LayerFECompositionState mLayerFEState;
     impl::OutputCompositionState mOutputState;
 };
 
@@ -112,27 +112,26 @@
     OutputLayerSourceCropTest() {
         // Set reasonable default values for a simple case. Each test will
         // set one specific value to something different.
-        mLayerState.frontEnd.geomUsesSourceCrop = true;
-        mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.transparentRegionHint = Region{};
-        mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
-        mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
-        mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomBufferTransform = TR_IDENT;
+        mLayerFEState.geomUsesSourceCrop = true;
+        mLayerFEState.geomContentCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferTransform = TR_IDENT;
 
         mOutputState.viewport = Rect{0, 0, 1920, 1080};
     }
 
     FloatRect calculateOutputSourceCrop() {
-        mLayerState.frontEnd.geomInverseLayerTransform =
-                mLayerState.frontEnd.geomLayerTransform.inverse();
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
 
         return mOutputLayer.calculateOutputSourceCrop();
     }
 };
 
 TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) {
-    mLayerState.frontEnd.geomUsesSourceCrop = false;
+    mLayerFEState.geomUsesSourceCrop = false;
 
     const FloatRect expected{};
     EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
@@ -144,15 +143,15 @@
 }
 
 TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
-    mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
 
     const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
     EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
 }
 
 TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
-    mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
-    mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
 
     const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
     EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
@@ -190,8 +189,8 @@
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
-        mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
+        mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
 
         EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i;
@@ -199,7 +198,7 @@
 }
 
 TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) {
-    mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 960, 540};
+    mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540};
 
     const FloatRect expected{0.f, 0.f, 960.f, 540.f};
     EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
@@ -221,20 +220,19 @@
         // Set reasonable default values for a simple case. Each test will
         // set one specific value to something different.
 
-        mLayerState.frontEnd.transparentRegionHint = Region{};
-        mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
-        mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
-        mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+        mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
         mOutputState.viewport = Rect{0, 0, 1920, 1080};
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
     Rect calculateOutputDisplayFrame() {
-        mLayerState.frontEnd.geomInverseLayerTransform =
-                mLayerState.frontEnd.geomLayerTransform.inverse();
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
 
         return mOutputLayer.calculateOutputDisplayFrame();
     }
@@ -246,32 +244,32 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
-    mLayerState.frontEnd.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
+    mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
     const Rect expected{0, 0, 0, 0};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
     const Rect expected{100, 200, 300, 500};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
-    mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
     const Rect expected{1420, 100, 1720, 300};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
 
 TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{};
+    mLayerFEState.geomCrop = Rect{};
     const Rect expected{0, 0, 1920, 1080};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
 
 TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
-    mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
+    mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
     const Rect expected{0, 0, 960, 540};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
@@ -293,7 +291,7 @@
  */
 
 TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) {
-    mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
 
     struct Entry {
         uint32_t layer;
@@ -340,8 +338,8 @@
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080);
-        mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+        mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
+        mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
 
         auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
@@ -351,7 +349,7 @@
 
 TEST_F(OutputLayerTest,
        calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) {
-    mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = true;
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = true;
 
     struct Entry {
         uint32_t layer;
@@ -398,8 +396,8 @@
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerState.frontEnd.geomLayerTransform = ui::Transform{entry.layer};
-        mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+        mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
+        mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
 
         auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
@@ -425,7 +423,7 @@
 struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
 public:
     OutputLayerUpdateCompositionStateTest() {
-        EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+        EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
         EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
         EXPECT_CALL(mOutput, getDisplayColorProfile())
                 .WillRepeatedly(Return(&mDisplayColorProfile));
@@ -458,8 +456,9 @@
 };
 
 TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
-    mLayerState.frontEnd.isSecure = true;
+    mLayerFEState.isSecure = true;
     mOutputState.isSecure = true;
+    mOutputLayer.editState().forceClientComposition = true;
 
     setupGeometryChildCallValues();
 
@@ -472,7 +471,7 @@
 
 TEST_F(OutputLayerUpdateCompositionStateTest,
        alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) {
-    mLayerState.frontEnd.isSecure = true;
+    mLayerFEState.isSecure = true;
     mOutputState.isSecure = false;
 
     setupGeometryChildCallValues();
@@ -486,7 +485,7 @@
 
 TEST_F(OutputLayerUpdateCompositionStateTest,
        alsoSetsForceCompositionIfUnsupportedBufferTransform) {
-    mLayerState.frontEnd.isSecure = true;
+    mLayerFEState.isSecure = true;
     mOutputState.isSecure = true;
 
     mBufferTransform = ui::Transform::ROT_INVALID;
@@ -501,12 +500,12 @@
 }
 
 TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
-    mLayerState.frontEnd.dataspace = ui::Dataspace::DISPLAY_P3;
+    mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
     mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
 
     // If the layer is not colorspace agnostic, the output layer dataspace
     // should use the layers requested colorspace.
-    mLayerState.frontEnd.isColorspaceAgnostic = false;
+    mLayerFEState.isColorspaceAgnostic = false;
 
     mOutputLayer.updateCompositionState(false);
 
@@ -514,7 +513,7 @@
 
     // If the layer is colorspace agnostic, the output layer dataspace
     // should use the colorspace chosen for the whole output.
-    mLayerState.frontEnd.isColorspaceAgnostic = true;
+    mLayerFEState.isColorspaceAgnostic = true;
 
     mOutputLayer.updateCompositionState(false);
 
@@ -522,13 +521,25 @@
 }
 
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
+    mOutputLayer.editState().forceClientComposition = false;
+
     mOutputLayer.updateCompositionState(false);
 
     EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
 }
 
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       doesNotClearForceClientCompositionIfNotDoingGeometry) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mOutputLayer.updateCompositionState(false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
 TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) {
-    mLayerState.frontEnd.forceClientComposition = true;
+    mLayerFEState.forceClientComposition = true;
+    mOutputLayer.editState().forceClientComposition = false;
 
     mOutputLayer.updateCompositionState(false);
 
@@ -537,6 +548,7 @@
 
 TEST_F(OutputLayerUpdateCompositionStateTest,
        clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) {
+    mOutputLayer.editState().forceClientComposition = false;
     EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
 
     mOutputLayer.updateCompositionState(false);
@@ -583,18 +595,18 @@
         outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
         outputLayerState.dataspace = kDataspace;
 
-        mLayerState.frontEnd.blendMode = kBlendMode;
-        mLayerState.frontEnd.alpha = kAlpha;
-        mLayerState.frontEnd.type = kType;
-        mLayerState.frontEnd.appId = kAppId;
-        mLayerState.frontEnd.colorTransform = kColorTransform;
-        mLayerState.frontEnd.color = kColor;
-        mLayerState.frontEnd.surfaceDamage = kSurfaceDamage;
-        mLayerState.frontEnd.hdrMetadata = kHdrMetadata;
-        mLayerState.frontEnd.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
-        mLayerState.frontEnd.buffer = kBuffer;
-        mLayerState.frontEnd.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
-        mLayerState.frontEnd.acquireFence = kFence;
+        mLayerFEState.blendMode = kBlendMode;
+        mLayerFEState.alpha = kAlpha;
+        mLayerFEState.type = kType;
+        mLayerFEState.appId = kAppId;
+        mLayerFEState.colorTransform = kColorTransform;
+        mLayerFEState.color = kColor;
+        mLayerFEState.surfaceDamage = kSurfaceDamage;
+        mLayerFEState.hdrMetadata = kHdrMetadata;
+        mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
+        mLayerFEState.buffer = kBuffer;
+        mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+        mLayerFEState.acquireFence = kFence;
 
         EXPECT_CALL(mOutput, getDisplayColorProfile())
                 .WillRepeatedly(Return(&mDisplayColorProfile));
@@ -698,7 +710,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     expectSetColorCall();
@@ -708,7 +720,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
 
     expectPerFrameCommonCalls();
     expectSetSidebandHandleCall();
@@ -718,7 +730,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
 
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
@@ -728,7 +740,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
 
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
@@ -741,7 +753,7 @@
     (*mOutputLayer.editState().hwc).hwcCompositionType =
             Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     expectSetColorCall();
@@ -751,7 +763,7 @@
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
     expectSetColorCall();
@@ -763,7 +775,7 @@
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
     mOutputLayer.editState().forceClientComposition = true;
 
-    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
     expectSetColorCall();
@@ -787,7 +799,7 @@
         auto& outputLayerState = mOutputLayer.editState();
         outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
 
-        mLayerState.frontEnd.cursorFrame = kDefaultCursorFrame;
+        mLayerFEState.cursorFrame = kDefaultCursorFrame;
 
         mOutputState.viewport = kDefaultDisplayViewport;
         mOutputState.transform = ui::Transform{kDefaultTransform};
@@ -812,7 +824,7 @@
 }
 
 TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCIntersectedWithViewport) {
-    mLayerState.frontEnd.cursorFrame = Rect{3000, 3000, 3016, 3016};
+    mLayerFEState.cursorFrame = Rect{3000, 3000, 3016, 3016};
 
     EXPECT_CALL(*mHwcLayer, setCursorPosition(1920, 1080)).WillOnce(Return(kDefaultError));
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 10ec1ee..70c343b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,7 +16,7 @@
 
 #include <cmath>
 
-#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -376,6 +376,71 @@
     EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
 }
 
+TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+    StrictMock<mock::Layer> layer;
+    LayerFECompositionState layerFEState;
+
+    EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
+
+    const uint32_t layerStack1 = 123u;
+    const uint32_t layerStack2 = 456u;
+
+    // If the output accepts layerStack1 and internal-only layers....
+    mOutput.setLayerStackFilter(layerStack1, true);
+
+    // A null layer pointer does not belong to the output
+    EXPECT_FALSE(mOutput.belongsInOutput(nullptr));
+
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    layerFEState.layerStackId = std::nullopt;
+    layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = std::nullopt;
+    layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    // Any layer with layerStack1 belongs to it, internal-only or not.
+    layerFEState.layerStackId = layerStack1;
+    layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack1;
+    layerFEState.internalOnly = true;
+    EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack2;
+    layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack2;
+    layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    // If the output accepts layerStack1 but not internal-only layers...
+    mOutput.setLayerStackFilter(layerStack1, false);
+
+    // A null layer pointer does not belong to the output
+    EXPECT_FALSE(mOutput.belongsInOutput(nullptr));
+
+    // Only non-internal layers with layerStack1 belong to it.
+    layerFEState.layerStackId = layerStack1;
+    layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack1;
+    layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack2;
+    layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+
+    layerFEState.layerStackId = layerStack2;
+    layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput.belongsInOutput(&layer));
+}
+
 /*
  * Output::getOutputLayerForLayer()
  */
@@ -667,8 +732,8 @@
     leftOutputLayerState.clearClientTarget = false;
     leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
 
-    impl::LayerCompositionState leftLayerState;
-    leftLayerState.frontEnd.isOpaque = true;
+    LayerFECompositionState leftLayerFEState;
+    leftLayerFEState.isOpaque = true;
 
     const half3 leftLayerColor{1.f, 0.f, 0.f};
     renderengine::LayerSettings leftLayerRESettings;
@@ -678,8 +743,8 @@
     rightOutputLayerState.clearClientTarget = false;
     rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
 
-    impl::LayerCompositionState rightLayerState;
-    rightLayerState.frontEnd.isOpaque = true;
+    LayerFECompositionState rightLayerFEState;
+    rightLayerFEState.isOpaque = true;
 
     const half3 rightLayerColor{0.f, 1.f, 0.f};
     renderengine::LayerSettings rightLayerRESettings;
@@ -690,7 +755,7 @@
     EXPECT_CALL(*leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
     EXPECT_CALL(*leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(*leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftLayer, getState()).WillRepeatedly(ReturnRef(leftLayerState));
+    EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
     EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
 
     EXPECT_CALL(*rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
@@ -698,7 +763,7 @@
     EXPECT_CALL(*rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
     EXPECT_CALL(*rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(*rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightLayer, getState()).WillRepeatedly(ReturnRef(rightLayerState));
+    EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
     EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
 
     Output::OutputLayers outputLayers;
@@ -741,15 +806,15 @@
     outputLayerState.clearClientTarget = false;
     outputLayerState.visibleRegion = Region{Rect{3000, 0, 4000, 1000}};
 
-    impl::LayerCompositionState layerState;
-    layerState.frontEnd.isOpaque = true;
+    LayerFECompositionState layerFEState;
+    layerFEState.isOpaque = true;
 
     EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
     EXPECT_CALL(*outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
     EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
     EXPECT_CALL(*outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(*outputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(layerState));
+    EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
     EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
 
     Output::OutputLayers outputLayers;
@@ -796,15 +861,15 @@
     leftOutputLayerState.clearClientTarget = true;
     leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
 
-    impl::LayerCompositionState leftLayerState;
-    leftLayerState.frontEnd.isOpaque = true;
+    LayerFECompositionState leftLayerFEState;
+    leftLayerFEState.isOpaque = true;
 
     impl::OutputLayerCompositionState rightOutputLayerState;
     rightOutputLayerState.clearClientTarget = true;
     rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
 
-    impl::LayerCompositionState rightLayerState;
-    rightLayerState.frontEnd.isOpaque = true;
+    LayerFECompositionState rightLayerFEState;
+    rightLayerFEState.isOpaque = true;
 
     const half3 rightLayerColor{0.f, 1.f, 0.f};
     renderengine::LayerSettings rightLayerRESettings;
@@ -816,14 +881,14 @@
     EXPECT_CALL(*leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
     EXPECT_CALL(*leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     EXPECT_CALL(*leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftLayer, getState()).WillRepeatedly(ReturnRef(leftLayerState));
+    EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
 
     EXPECT_CALL(*rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
     EXPECT_CALL(*rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
     EXPECT_CALL(*rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
     EXPECT_CALL(*rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     EXPECT_CALL(*rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightLayer, getState()).WillRepeatedly(ReturnRef(rightLayerState));
+    EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
     EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
 
     Output::OutputLayers outputLayers;
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index d40a38c..4a11303 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -30,8 +30,4 @@
     return false;
 }
 
-bool ContainerLayer::canReceiveInput() const {
-    return !isHiddenByPolicy();
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index f0fbb61..c3624f6 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -31,8 +31,6 @@
     const char* getType() const override { return "ContainerLayer"; }
     bool isVisible() const override;
 
-    bool canReceiveInput() const override;
-
     bool isCreatedFromMainThread() const override { return true; }
 };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8f6672c..ba948cf 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -27,7 +27,6 @@
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
@@ -990,7 +989,7 @@
         // create background color layer if one does not yet exist
         uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
         const String8& name = mName + "BackgroundColorLayer";
-        mCurrentState.bgColorLayer = new ColorLayer(
+        mCurrentState.bgColorLayer = mFlinger->getFactory().createColorLayer(
                 LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata()));
 
         // add to child list
@@ -1994,7 +1993,7 @@
 }
 
 bool Layer::canReceiveInput() const {
-    return isVisible();
+    return !isHiddenByPolicy();
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 740c8c4..536464e 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -32,6 +32,7 @@
     ~DispSyncSource() override = default;
 
     // The following methods are implementation of VSyncSource.
+    const char* getName() const override { return mName; }
     void setVSyncEnabled(bool enable) override;
     void setCallback(VSyncSource::Callback* callback) override;
     void setPhaseOffset(nsecs_t phaseOffset) override;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9d1f777..8d9adc8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -154,23 +154,11 @@
 
 namespace impl {
 
-EventThread::EventThread(std::unique_ptr<VSyncSource> src,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
-                         const char* threadName)
-      : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : mVSyncSource(src),
-        mVSyncSourceUnique(std::move(uniqueSrc)),
+EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+                         InterceptVSyncsCallback interceptVSyncsCallback)
+      : mVSyncSource(std::move(vsyncSource)),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
-        mThreadName(threadName) {
-    if (src == nullptr) {
-        mVSyncSource = mVSyncSourceUnique.get();
-    }
+        mThreadName(mVSyncSource->getName()) {
     mVSyncSource->setCallback(this);
 
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -178,7 +166,7 @@
         threadMain(lock);
     });
 
-    pthread_setname_np(mThread.native_handle(), threadName);
+    pthread_setname_np(mThread.native_handle(), mThreadName);
 
     pid_t tid = pthread_gettid_np(mThread.native_handle());
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index dd23b88..a029586 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -62,6 +62,8 @@
     };
 
     virtual ~VSyncSource() {}
+
+    virtual const char* getName() const = 0;
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
     virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
@@ -126,9 +128,7 @@
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
 
-    // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
-    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
+    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -157,10 +157,6 @@
 
     using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
 
-    // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
-
     void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
 
     bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
@@ -174,9 +170,7 @@
     // Implements VSyncSource::Callback
     void onVSyncEvent(nsecs_t timestamp) override;
 
-    // TODO(b/128863962): Once the Scheduler is complete this pointer will become obsolete.
-    VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
-    std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
+    const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 90609af..6c502e6 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -42,6 +42,7 @@
         }
     }
 
+    const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
     void pauseVsyncCallback(bool) {}
@@ -51,4 +52,4 @@
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index a733781..17afdda 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -109,7 +109,7 @@
         bool isLowActivityLayer() const {
             // We want to make sure that we received more than two frames from the layer
             // in order to check low activity.
-            if (mElements.size() < 2) {
+            if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
                 return false;
             }
 
@@ -118,7 +118,8 @@
             // Check the frame before last to determine whether there is low activity.
             // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
             // infrequent updates.
-            if (mElements.at(mElements.size() - 2) < obsoleteEpsilon) {
+            if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
+                obsoleteEpsilon) {
                 return true;
             }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index fcb307f..5318b00 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -85,24 +85,6 @@
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(android::EventThread* eventThread,
-                                  ResyncCallback resyncCallback) {
-    if (mEventThread == eventThread) {
-        return;
-    }
-
-    if (mEventTube.getFd() >= 0) {
-        mLooper->removeFd(mEventTube.getFd());
-    }
-
-    mEventThread = eventThread;
-    mEvents = eventThread->createEventConnection(std::move(resyncCallback),
-                                                 ISurfaceComposer::eConfigChangedSuppress);
-    mEvents->stealReceiveChannel(&mEventTube);
-    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                   this);
-}
-
 void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
     if (mEventTube.getFd() >= 0) {
         mLooper->removeFd(mEventTube.getFd());
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 0b2206d..fcfc4aa 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -85,8 +85,6 @@
     virtual ~MessageQueue();
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
-    // TODO(b/128863962): Remove this function once everything is migrated to Scheduler.
-    virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0;
     virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
@@ -115,7 +113,6 @@
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    android::EventThread* mEventThread;
     sp<EventThreadConnection> mEvents;
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
@@ -126,7 +123,6 @@
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
-    void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 04e902b..6be88f8 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -51,7 +51,6 @@
     const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
     const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
 
-    mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
     mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
     mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
 }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 9d47749..2fd100f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -41,10 +41,9 @@
  */
 class RefreshRateConfigs {
 public:
-    // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
-    // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+    // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
     // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
-    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+    enum class RefreshRateType { DEFAULT, PERFORMANCE };
 
     struct RefreshRate {
         // This config ID corresponds to the position of the config in the vector that is stored
@@ -54,26 +53,57 @@
         std::string name;
         // Refresh rate in frames per second, rounded to the nearest integer.
         uint32_t fps = 0;
-        // config Id (returned from HWC2::Display::Config::getId())
-        hwc2_config_t id;
+        // Vsync period in nanoseconds.
+        nsecs_t vsyncPeriod;
+        // Hwc config Id (returned from HWC2::Display::Config::getId())
+        hwc2_config_t hwcId;
     };
 
+    // Returns true if this device is doing refresh rate switching. This won't change at runtime.
+    bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+
+    // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+    // from multiple threads. This can only be called if refreshRateSwitching() returns true.
     // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
     // baking them in.
-    const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
-        return mRefreshRates;
-    }
-    std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
-        const auto& refreshRate = mRefreshRates.find(type);
-        if (refreshRate != mRefreshRates.end()) {
-            return refreshRate->second;
-        }
-        return nullptr;
+    const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
+        LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
+        return mRefreshRateMap;
     }
 
-    RefreshRateType getRefreshRateType(hwc2_config_t id) const {
-        for (const auto& [type, refreshRate] : mRefreshRates) {
-            if (refreshRate->id == id) {
+    const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
+        if (!mRefreshRateSwitchingSupported) {
+            return getCurrentRefreshRate().second;
+        } else {
+            auto refreshRate = mRefreshRateMap.find(type);
+            LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
+            return refreshRate->second;
+        }
+    }
+
+    std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
+        int currentConfig = mCurrentConfig;
+        if (mRefreshRateSwitchingSupported) {
+            for (const auto& [type, refresh] : mRefreshRateMap) {
+                if (refresh.configId == currentConfig) {
+                    return {type, refresh};
+                }
+            }
+            LOG_ALWAYS_FATAL();
+        }
+        return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
+    }
+
+    const RefreshRate& getRefreshRateFromConfigId(int configId) const {
+        LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
+        return mRefreshRates[configId];
+    }
+
+    RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
+        if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+
+        for (const auto& [type, refreshRate] : mRefreshRateMap) {
+            if (refreshRate.hwcId == hwcId) {
                 return type;
             }
         }
@@ -81,64 +111,102 @@
         return RefreshRateType::DEFAULT;
     }
 
-    void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
-        mRefreshRates.clear();
+    void setCurrentConfig(int config) {
+        LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
+        mCurrentConfig = config;
+    }
 
-        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
-        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
-                              std::make_shared<RefreshRate>(
-                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
-                                                  HWC2_SCREEN_OFF_CONFIG_ID}));
+    struct InputConfig {
+        hwc2_config_t hwcId = 0;
+        nsecs_t vsyncPeriod = 0;
+    };
 
-        if (configs.size() < 1) {
-            ALOGE("Device does not have valid configs. Config size is 0.");
-            return;
+    RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+                       int currentConfig) {
+        init(refreshRateSwitching, configs, currentConfig);
+    }
+
+    RefreshRateConfigs(bool refreshRateSwitching,
+                       const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+                       int currentConfig) {
+        std::vector<InputConfig> inputConfigs;
+        for (const auto& config : configs) {
+            inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
         }
-
-        // Create a map between config index and vsync period. This is all the info we need
-        // from the configs.
-        std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
-        for (int i = 0; i < configs.size(); ++i) {
-            configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
-        }
-
-        std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
-                  [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
-                      return a.second > b.second;
-                  });
-
-        // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
-        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
-        if (vsyncPeriod != 0) {
-            const float fps = 1e9 / vsyncPeriod;
-            const int configId = configIdToVsyncPeriod[0].first;
-            mRefreshRates.emplace(RefreshRateType::DEFAULT,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
-
-        if (configs.size() < 2) {
-            return;
-        }
-
-        // When the configs are ordered by the resync rate. We assume that the second one is
-        // PERFORMANCE, eg. the higher rate.
-        vsyncPeriod = configIdToVsyncPeriod[1].second;
-        if (vsyncPeriod != 0) {
-            const float fps = 1e9 / vsyncPeriod;
-            const int configId = configIdToVsyncPeriod[1].first;
-            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
+        init(refreshRateSwitching, inputConfigs, currentConfig);
     }
 
 private:
-    std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
+    void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+              int currentConfig) {
+        mRefreshRateSwitchingSupported = refreshRateSwitching;
+        LOG_ALWAYS_FATAL_IF(configs.empty());
+        LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
+        mCurrentConfig = currentConfig;
+
+        auto buildRefreshRate = [&](int configId) -> RefreshRate {
+            const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
+            const float fps = 1e9 / vsyncPeriod;
+            return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
+                    vsyncPeriod, configs[configId].hwcId};
+        };
+
+        for (int i = 0; i < configs.size(); ++i) {
+            mRefreshRates.push_back(buildRefreshRate(i));
+        }
+
+        if (!mRefreshRateSwitchingSupported) return;
+
+        auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
+            if (configs.size() < 2) {
+                return {};
+            }
+
+            std::vector<const RefreshRate*> sortedRefreshRates;
+            for (const auto& refreshRate : mRefreshRates) {
+                sortedRefreshRates.push_back(&refreshRate);
+            }
+            std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
+                      [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
+                          return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+                      });
+
+            // When the configs are ordered by the resync rate, we assume that
+            // the first one is DEFAULT and the second one is PERFORMANCE,
+            // i.e. the higher rate.
+            if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
+                sortedRefreshRates[1]->vsyncPeriod == 0) {
+                return {};
+            }
+
+            return std::pair<int, int>(sortedRefreshRates[0]->configId,
+                                       sortedRefreshRates[1]->configId);
+        };
+
+        auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
+        if (!defaultAndPerfConfigs) {
+            mRefreshRateSwitchingSupported = false;
+            return;
+        }
+
+        mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
+        mRefreshRateMap[RefreshRateType::PERFORMANCE] =
+                mRefreshRates[defaultAndPerfConfigs->second];
+    }
+
+    // Whether this device is doing refresh rate switching or not. This must not change after this
+    // object is initialized.
+    bool mRefreshRateSwitchingSupported;
+    // The list of refresh rates, indexed by display config ID. This must not change after this
+    // object is initialized.
+    std::vector<RefreshRate> mRefreshRates;
+    // The mapping of refresh rate type to RefreshRate. This must not change after this object is
+    // initialized.
+    std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
+    // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
+    // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
+    // atomic.
+    std::atomic<int> mCurrentConfig;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 1f097db..8afc93e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -41,21 +41,18 @@
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
 public:
-    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
-          : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
+    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
+                     int currentConfigMode, int currentPowerMode)
+          : mRefreshRateConfigs(refreshRateConfigs),
+            mTimeStats(timeStats),
+            mCurrentConfigMode(currentConfigMode),
+            mCurrentPowerMode(currentPowerMode) {}
 
-    // Sets power mode. We only collect the information when the power mode is not
-    // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
-    // on config mode.
+    // Sets power mode.
     void setPowerMode(int mode) {
         if (mCurrentPowerMode == mode) {
             return;
         }
-        // If power mode is normal, the time is going to be recorded under config modes.
-        if (mode == HWC_POWER_MODE_NORMAL) {
-            mCurrentPowerMode = mode;
-            return;
-        }
         flushTime();
         mCurrentPowerMode = mode;
     }
@@ -79,16 +76,15 @@
         flushTime();
 
         std::unordered_map<std::string, int64_t> totalTime;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            int64_t totalTimeForConfig = 0;
-            if (!config) {
-                continue;
-            }
-            if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
-                totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
-            }
-            totalTime[config->name] = totalTimeForConfig;
+        // Multiple configs may map to the same name, e.g. "60fps". Add the
+        // times for such configs together.
+        for (const auto& [config, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
         }
+        for (const auto& [config, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+        }
+        totalTime["ScreenOff"] = mScreenOffTime;
         return totalTime;
     }
 
@@ -104,32 +100,26 @@
     }
 
 private:
-    void flushTime() {
-        // Normal power mode is counted under different config modes.
-        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
-            flushTimeForMode(mCurrentConfigMode);
-        } else {
-            flushTimeForMode(SCREEN_OFF_CONFIG_ID);
-        }
-    }
-
     // Calculates the time that passed in ms between the last time we recorded time and the time
     // this method was called.
-    void flushTimeForMode(int mode) {
+    void flushTime() {
         nsecs_t currentTime = systemTime();
         nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
         int64_t timeElapsedMs = ns2ms(timeElapsed);
         mPreviousRecordedTime = currentTime;
 
-        mConfigModesTotalTime[mode] += timeElapsedMs;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            if (!config) {
-                continue;
+        uint32_t fps = 0;
+        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+            // Normal power mode is counted under different config modes.
+            if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
+                mConfigModesTotalTime[mCurrentConfigMode] = 0;
             }
-            if (config->configId == mode) {
-                mTimeStats.recordRefreshRate(config->fps, timeElapsed);
-            }
+            mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
+            fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+        } else {
+            mScreenOffTime += timeElapsedMs;
         }
+        mTimeStats.recordRefreshRate(fps, timeElapsed);
     }
 
     // Formats the time in milliseconds into easy to read format.
@@ -149,10 +139,11 @@
     // Aggregate refresh rate statistics for telemetry.
     TimeStats& mTimeStats;
 
-    int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
-    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+    int mCurrentConfigMode;
+    int32_t mCurrentPowerMode;
 
-    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    int64_t mScreenOffTime = 0;
 
     nsecs_t mPreviousRecordedTime = systemTime();
 };
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e2a880a..d60e101 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -41,6 +41,7 @@
 #include "DispSyncSource.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
+#include "InjectVSyncSource.h"
 #include "OneShotTimer.h"
 #include "SchedulerUtils.h"
 #include "SurfaceFlingerProperties.h"
@@ -116,54 +117,43 @@
     return *mPrimaryDispSync;
 }
 
-Scheduler::ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
-        ResyncCallback resyncCallback,
-        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    auto eventThread = makeEventThread(connectionName, phaseOffsetNs, offsetThresholdForNextVsync,
-                                       std::move(interceptCallback));
-    return createConnection(std::move(eventThread), std::move(resyncCallback));
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
+        const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) {
+    return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
+                                            offsetThresholdForNextVsync, true /* traceVsync */,
+                                            name);
 }
 
-Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread,
-                                                        ResyncCallback&& resyncCallback) {
+Scheduler::ConnectionHandle Scheduler::createConnection(
+        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+    auto vsyncSource =
+            makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                           std::move(interceptCallback));
+    return createConnection(std::move(eventThread));
+}
+
+Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
     const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
     ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
 
-    auto connection = createConnectionInternal(eventThread.get(), std::move(resyncCallback),
-                                               ISurfaceComposer::eConfigChangedSuppress);
+    auto connection =
+            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
 
     mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
     return handle;
 }
 
-std::unique_ptr<EventThread> Scheduler::makeEventThread(
-        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
-        impl::EventThread::InterceptVSyncsCallback&& interceptCallback) {
-    auto source = std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
-                                                   offsetThresholdForNextVsync,
-                                                   true /* traceVsync */, connectionName);
-    return std::make_unique<impl::EventThread>(std::move(source), std::move(interceptCallback),
-                                               connectionName);
-}
-
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ResyncCallback&& resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
-    return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
+        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
+    return eventThread->createEventConnection([&] { resync(); }, configChanged);
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        ConnectionHandle handle, ResyncCallback resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
+        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return createConnectionInternal(mConnections[handle].thread.get(), std::move(resyncCallback),
-                                    configChanged);
-}
-
-EventThread* Scheduler::getEventThread(ConnectionHandle handle) {
-    RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return mConnections[handle].thread.get();
+    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
 }
 
 sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -208,6 +198,37 @@
     stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
 }
 
+Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+    if (mInjectVSyncs == enable) {
+        return {};
+    }
+
+    ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
+
+    if (!mInjectorConnectionHandle) {
+        auto vsyncSource = std::make_unique<InjectVSyncSource>();
+        mVSyncInjector = vsyncSource.get();
+
+        auto eventThread =
+                std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                    impl::EventThread::InterceptVSyncsCallback());
+
+        mInjectorConnectionHandle = createConnection(std::move(eventThread));
+    }
+
+    mInjectVSyncs = enable;
+    return mInjectorConnectionHandle;
+}
+
+bool Scheduler::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs || !mVSyncInjector) {
+        return false;
+    }
+
+    mVSyncInjector->onInjectSyncEvent(when);
+    return true;
+}
+
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -248,23 +269,15 @@
     setVsyncPeriod(period);
 }
 
-ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
-    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
-    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
-        if (const auto vsync = ptr.lock()) {
-            vsync->resync(getVsyncPeriod);
-        }
-    };
-}
-
-void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
-    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+void Scheduler::resync() {
+    static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
 
     const nsecs_t now = systemTime();
-    const nsecs_t last = lastResyncTime.exchange(now);
+    const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+        resyncToHardwareVsync(false,
+                              mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
     }
 }
 
@@ -314,15 +327,19 @@
 
 std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
         std::string const& name, int windowType) {
-    RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
-            ? RefreshRateType::DEFAULT
-            : RefreshRateType::PERFORMANCE;
-
-    const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
-    const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;
-
-    const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
-    const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
+    uint32_t defaultFps, performanceFps;
+    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+        performanceFps =
+                mRefreshRateConfigs
+                        .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
+                                                        ? RefreshRateType::DEFAULT
+                                                        : RefreshRateType::PERFORMANCE)
+                        .fps;
+    } else {
+        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
+        performanceFps = defaultFps;
+    }
     return mLayerHistory.createLayer(name, defaultFps, performanceFps);
 }
 
@@ -368,16 +385,6 @@
     mChangeRefreshRateCallback = std::move(callback);
 }
 
-void Scheduler::setGetCurrentRefreshRateTypeCallback(GetCurrentRefreshRateTypeCallback&& callback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetCurrentRefreshRateTypeCallback = std::move(callback);
-}
-
-void Scheduler::setGetVsyncPeriodCallback(GetVsyncPeriod&& callback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetVsyncPeriod = std::move(callback);
-}
-
 void Scheduler::resetIdleTimer() {
     if (mIdleTimer) {
         mIdleTimer->reset();
@@ -416,16 +423,13 @@
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
     ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (!mGetCurrentRefreshRateTypeCallback || !mGetVsyncPeriod) return;
-
-    const auto type = mGetCurrentRefreshRateTypeCallback();
-    if (state == TimerState::Reset && type == RefreshRateType::PERFORMANCE) {
+    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        resyncToHardwareVsync(true /* makeAvailable */, mGetVsyncPeriod());
-    } else if (state == TimerState::Expired && type != RefreshRateType::PERFORMANCE) {
+        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod);
+    } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
         // need to update the DispSync model anyway.
@@ -485,6 +489,10 @@
 }
 
 Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+    if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+        return RefreshRateType::DEFAULT;
+    }
+
     // HDR content is not supported on PERFORMANCE mode
     if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
         return RefreshRateType::DEFAULT;
@@ -514,16 +522,11 @@
     // Content detection is on, find the appropriate refresh rate with minimal error
     // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
     const float rate = static_cast<float>(mFeatures.contentRefreshRate);
-    auto begin = mRefreshRateConfigs.getRefreshRates().cbegin();
-
-    // Skip POWER_SAVING config as it is not a real config
-    if (begin->first == RefreshRateType::POWER_SAVING) {
-        ++begin;
-    }
-    auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(),
+    auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
+                            mRefreshRateConfigs.getRefreshRateMap().cend(),
                             [rate](const auto& lhs, const auto& rhs) -> bool {
-                                return std::abs(lhs.second->fps - rate) <
-                                        std::abs(rhs.second->fps - rate);
+                                return std::abs(lhs.second.fps - rate) <
+                                        std::abs(rhs.second.fps - rate);
                             });
     RefreshRateType currRefreshRateType = iter->first;
 
@@ -531,10 +534,10 @@
     // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
     // align well with both
     constexpr float MARGIN = 0.05f;
-    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / rate;
+    float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
     if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
-            ratio = iter->second->fps / rate;
+        while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
+            ratio = iter->second.fps / rate;
 
             if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
                 currRefreshRateType = iter->first;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 34e527c..a5971fe 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,7 @@
 
 class DispSync;
 class FenceTime;
+class InjectVSyncSource;
 struct DisplayStateInfo;
 
 class Scheduler {
@@ -43,9 +44,7 @@
     using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
     using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
     using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
-    using GetVsyncPeriod = std::function<nsecs_t()>;
 
     // Indicates whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
@@ -59,13 +58,12 @@
 
     using ConnectionHandle = scheduler::ConnectionHandle;
     ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
-                                      nsecs_t offsetThresholdForNextVsync, ResyncCallback,
+                                      nsecs_t offsetThresholdForNextVsync,
                                       impl::EventThread::InterceptVSyncsCallback);
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle, ResyncCallback,
+    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
                                                              ISurfaceComposer::ConfigChanged);
 
-    EventThread* getEventThread(ConnectionHandle);
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
@@ -79,6 +77,12 @@
 
     void getDisplayStatInfo(DisplayStatInfo* stats);
 
+    // Returns injector handle if injection has toggled, or an invalid handle otherwise.
+    ConnectionHandle enableVSyncInjection(bool enable);
+
+    // Returns false if injection is disabled.
+    bool injectVSync(nsecs_t when);
+
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
 
@@ -88,7 +92,7 @@
     // no-op.
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
-    ResyncCallback makeResyncCallback(GetVsyncPeriod&&);
+    void resync();
 
     // Passes a vsync sample to DispSync. periodFlushed will be true if
     // DispSync detected that the vsync period changed, and false otherwise.
@@ -113,9 +117,6 @@
     // Called by Scheduler to change refresh rate.
     void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
 
-    void setGetCurrentRefreshRateTypeCallback(GetCurrentRefreshRateTypeCallback&&);
-    void setGetVsyncPeriodCallback(GetVsyncPeriod&&);
-
     bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
@@ -143,14 +144,12 @@
     Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
               const scheduler::RefreshRateConfigs&);
 
-    // Creates a connection on the given EventThread and forwards the given callbacks.
-    std::unique_ptr<EventThread> makeEventThread(const char* connectionName, nsecs_t phaseOffsetNs,
-                                                 nsecs_t offsetThresholdForNextVsync,
-                                                 impl::EventThread::InterceptVSyncsCallback&&);
+    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
+                                                           nsecs_t offsetThresholdForNextVsync);
 
-    // Create a connection on the given EventThread and forward the resync callback.
-    ConnectionHandle createConnection(std::unique_ptr<EventThread>, ResyncCallback&&);
-    sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
+    // Create a connection on the given EventThread.
+    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
+    sp<EventThreadConnection> createConnectionInternal(EventThread*,
                                                        ISurfaceComposer::ConfigChanged);
 
     // Update feature state machine to given state when corresponding timer resets or expires.
@@ -178,21 +177,15 @@
     ConnectionHandle::Id mNextConnectionHandleId = 0;
     std::unordered_map<ConnectionHandle, Connection> mConnections;
 
+    bool mInjectVSyncs = false;
+    InjectVSyncSource* mVSyncInjector = nullptr;
+    ConnectionHandle mInjectorConnectionHandle;
+
     std::mutex mHWVsyncLock;
     bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
     bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
 
-    // Stores per-display state about VSYNC.
-    struct VsyncState {
-        explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
-
-        void resync(const GetVsyncPeriod&);
-
-        Scheduler& scheduler;
-        std::atomic<nsecs_t> lastResyncTime = 0;
-    };
-
-    const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+    std::atomic<nsecs_t> mLastResyncTime = 0;
 
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
@@ -211,9 +204,7 @@
     std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
 
     std::mutex mCallbackLock;
-    GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
     ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
-    GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index ab0c0ff..3b7567c 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -40,21 +40,17 @@
 
 using namespace std::chrono_literals;
 
-// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
-// the config is not visible to SF, and is completely maintained by HWC. However, we would
-// still like to keep track of time when the device is in this config.
-static constexpr int SCREEN_OFF_CONFIG_ID = -1;
-static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
-
 // This number is used when we try to determine how long do we keep layer information around
 // before we remove it. It is also used to determine how long the layer stays relevant.
 // This time period captures infrequent updates when playing YouTube video with static image,
 // or waiting idle in messaging app, when cursor is blinking.
 static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
 
-// Layer is considered low activity if the buffers come more than LOW_ACTIVITY_EPSILON_NS
-// apart. This is helping SF to vote for lower refresh rates when there is not activity
+// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
+// LOW_ACTIVITY_EPSILON_NS  apart.
+// This is helping SF to vote for lower refresh rates when there is not activity
 // in screen.
+static constexpr int LOW_ACTIVITY_BUFFERS = 2;
 static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
 
 // Calculates the statistical mean (average) in the data structure (array, vector). The
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cc34f50..a9632ec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -41,12 +41,9 @@
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <dvr/vr_flinger.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
@@ -104,7 +101,6 @@
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/InjectVSyncSource.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
@@ -258,7 +254,7 @@
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
-        mTimeStats(mFactory.createTimeStats()),
+        mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(std::make_unique<FrameTracer>()),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
@@ -549,14 +545,16 @@
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
 
-        // set the refresh rate according to the policy
-        const auto& performanceRefreshRate =
-                mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
+        if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+            // set the refresh rate according to the policy
+            const auto& performanceRefreshRate =
+                    mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
 
-        if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
-            setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
-        } else {
-            setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+            if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
+                setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
+            } else {
+                setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+            }
         }
     }));
 }
@@ -595,37 +593,9 @@
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-
     ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
 
     Mutex::Autolock _l(mStateLock);
-    // start the EventThread
-    mScheduler =
-            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         mRefreshRateConfigs);
-    auto resyncCallback =
-            mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
-    mAppConnectionHandle =
-            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback,
-                                         impl::EventThread::InterceptVSyncsCallback());
-    mSfConnectionHandle =
-            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback, [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
-
-    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-
-    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
-                            mPhaseOffsets->getCurrentOffsets());
-
-    mRegionSamplingThread =
-            new RegionSamplingThread(*this, *mScheduler,
-                                     RegionSamplingThread::EnvironmentTimingTunables());
 
     // Get a RenderEngine for the given display / config (can't fail)
     int32_t renderEngineFeature = 0;
@@ -700,37 +670,6 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
-    mScheduler->setChangeRefreshRateCallback(
-            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
-                Mutex::Autolock lock(mStateLock);
-                setRefreshRateTo(type, event);
-            });
-    mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        const auto display = getDefaultDisplayDeviceLocked();
-        if (!display) {
-            // If we don't have a default display the fallback to the default
-            // refresh rate type
-            return RefreshRateType::DEFAULT;
-        }
-
-        const int configId = display->getActiveConfig();
-        for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
-            if (refresh && refresh->configId == configId) {
-                return type;
-            }
-        }
-        // This should never happen, but just gracefully fallback to default.
-        return RefreshRateType::DEFAULT;
-    });
-    mScheduler->setGetVsyncPeriodCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
-    mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
-    mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
-
     ALOGV("Done initializing");
 }
 
@@ -888,7 +827,8 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+        const auto refreshRateType =
+                mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
         const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
         info.appVsyncOffset = offset.late.app;
 
@@ -987,7 +927,8 @@
     }
 
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
+    mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+    mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
 
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
@@ -1257,51 +1198,20 @@
 
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
 
-        if (mInjectVSyncs == enable) {
-            return;
+        if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
+            mEventQueue->setEventConnection(
+                    mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
         }
-
-        auto resyncCallback =
-                mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
-        // TODO(b/128863962): Part of the Injector should be refactored, so that it
-        // can be passed to Scheduler.
-        if (enable) {
-            ALOGV("VSync Injections enabled");
-            if (mVSyncInjector.get() == nullptr) {
-                mVSyncInjector = std::make_unique<InjectVSyncSource>();
-                mInjectorEventThread = std::make_unique<
-                        impl::EventThread>(mVSyncInjector.get(),
-                                           impl::EventThread::InterceptVSyncsCallback(),
-                                           "injEventThread");
-            }
-            mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
-        } else {
-            ALOGV("VSync Injections disabled");
-            mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
-                                        std::move(resyncCallback));
-        }
-
-        mInjectVSyncs = enable;
     }));
 
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
-    Mutex::Autolock _l(mStateLock);
-
-    if (!mInjectVSyncs) {
-        ALOGE("VSync Injections not enabled");
-        return BAD_VALUE;
-    }
-    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
-        ALOGV("Injecting VSync inside SurfaceFlinger");
-        mVSyncInjector->onInjectSyncEvent(when);
-    }
-    return NO_ERROR;
+    Mutex::Autolock lock(mStateLock);
+    return mScheduler->injectVSync(when) ? NO_ERROR : BAD_VALUE;
 }
 
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
@@ -1392,16 +1302,10 @@
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
-    auto resyncCallback = mScheduler->makeResyncCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
     const auto& handle =
             vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
 
-    return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
-                                                    configChanged);
+    return mScheduler->createDisplayEventConnection(handle, configChanged);
 }
 
 // ----------------------------------------------------------------------------
@@ -1498,13 +1402,8 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
-    if (!refreshRateConfig) {
-        ALOGV("Skipping refresh rate change request for unsupported rate.");
-        return;
-    }
-
-    const int desiredConfigId = refreshRateConfig->configId;
+    const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
+    const int desiredConfigId = refreshRateConfig.configId;
 
     if (!isDisplayConfigAllowed(desiredConfigId)) {
         ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
@@ -1664,7 +1563,8 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
@@ -1673,7 +1573,15 @@
             ? mPreviousPresentFences[0]
             : mPreviousPresentFences[1];
 
-    return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
+    if (fence == Fence::NO_FENCE) {
+        return false;
+    }
+
+    if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) {
+        fence->wait(graceTimeMs);
+    }
+
+    return (fence->getStatus() == Fence::Status::Unsignaled);
 }
 
 void SurfaceFlinger::populateExpectedPresentTime() {
@@ -1696,7 +1604,17 @@
             // seeing this same value.
             populateExpectedPresentTime();
 
-            const TracedOrdinal<bool> frameMissed = {"FrameMissed", previousFrameMissed()};
+            // When Backpressure propagation is enabled we want to give a small grace period
+            // for the present fence to fire instead of just giving up on this frame to handle cases
+            // where present fence is just about to get signaled.
+            const int graceTimeForPresentFenceMs =
+                    (mPropagateBackpressure &&
+                     (mPropagateBackpressureClientComposition || !mHadClientComposition))
+                    ? 1
+                    : 0;
+            const TracedOrdinal<bool> frameMissed = {"FrameMissed",
+                                                     previousFrameMissed(
+                                                             graceTimeForPresentFenceMs)};
             const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
                                                         mHadDeviceComposition && frameMissed};
             const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
@@ -1788,6 +1706,7 @@
     mRefreshPending = false;
 
     compositionengine::CompositionRefreshArgs refreshArgs;
+    refreshArgs.outputs.reserve(mDisplays.size());
     for (const auto& [_, display] : mDisplays) {
         refreshArgs.outputs.push_back(display->getCompositionDisplay());
     }
@@ -1795,13 +1714,21 @@
         auto compositionLayer = layer->getCompositionLayer();
         if (compositionLayer) refreshArgs.layers.push_back(compositionLayer);
     });
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (sp<Layer> layer : mLayersWithQueuedFrames) {
+        auto compositionLayer = layer->getCompositionLayer();
+        if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+    }
+
     refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
     refreshArgs.outputColorSetting = useColorManagement
             ? mDisplayColorSetting
             : compositionengine::OutputColorSetting::kUnmanaged;
     refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
     refreshArgs.forceOutputColorMode = mForceColorMode;
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid;
+
+    refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -1815,13 +1742,10 @@
                 std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
     }
 
-    mCompositionEngine->preComposition(refreshArgs);
-    rebuildLayerStacks();
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid; // Can be set by rebuildLayerStacks()
-    mCompositionEngine->present(refreshArgs);
-
     mGeometryInvalid = false;
 
+    mCompositionEngine->present(refreshArgs);
+
     postFrame();
     postComposition();
 
@@ -2083,77 +2007,6 @@
     }
 }
 
-void SurfaceFlinger::rebuildLayerStacks() {
-    ATRACE_CALL();
-    ALOGV("rebuildLayerStacks");
-
-    // rebuild the visible layer list per screen
-    if (CC_UNLIKELY(mVisibleRegionsDirty)) {
-        ATRACE_NAME("rebuildLayerStacks VR Dirty");
-        invalidateHwcGeometry();
-
-        std::vector<sp<compositionengine::LayerFE>> layersWithQueuedFrames;
-        layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (sp<Layer> layer : mLayersWithQueuedFrames) {
-            layersWithQueuedFrames.push_back(layer);
-        }
-
-        for (const auto& pair : mDisplays) {
-            const auto& displayDevice = pair.second;
-            auto display = displayDevice->getCompositionDisplay();
-            const auto& displayState = display->getState();
-            Region opaqueRegion;
-            Region dirtyRegion;
-            compositionengine::Output::OutputLayers layersSortedByZ;
-            const ui::Transform& tr = displayState.transform;
-            const Rect bounds = displayState.bounds;
-            if (!displayState.isEnabled) {
-                continue;
-            }
-
-            computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion, layersSortedByZ);
-
-            // computeVisibleRegions walks the layers in reverse-Z order. Reverse
-            // to get a back-to-front ordering.
-            std::reverse(layersSortedByZ.begin(), layersSortedByZ.end());
-
-            display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
-
-            compositionengine::Output::ReleasedLayers releasedLayers;
-            if (displayDevice->getId() && !layersWithQueuedFrames.empty()) {
-                // For layers that are being removed from a HWC display,
-                // and that have queued frames, add them to a a list of
-                // released layers so we can properly set a fence.
-
-                // Any non-null entries in the current list of layers are layers
-                // that are no longer going to be visible
-                for (auto& layer : display->getOutputLayersOrderedByZ()) {
-                    if (!layer) {
-                        continue;
-                    }
-
-                    sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE());
-                    const bool hasQueuedFrames =
-                            std::find(layersWithQueuedFrames.cbegin(),
-                                      layersWithQueuedFrames.cend(),
-                                      layerFE) != layersWithQueuedFrames.cend();
-
-                    if (hasQueuedFrames) {
-                        releasedLayers.emplace_back(layerFE);
-                    }
-                }
-            }
-            display->setReleasedLayers(std::move(releasedLayers));
-
-            Region undefinedRegion{bounds};
-            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-
-            display->editState().undefinedRegion = undefinedRegion;
-            display->editState().dirtyRegion.orSelf(dirtyRegion);
-        }
-    }
-}
-
 void SurfaceFlinger::postFrame()
 {
     // |mStateLock| not needed as we are on the main thread
@@ -2206,6 +2059,9 @@
         if (event.connection == HWC2::Connection::Connected) {
             if (!mPhysicalDisplayTokens.count(info->id)) {
                 ALOGV("Creating display %s", to_string(info->id).c_str());
+                if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+                    initScheduler(info->id);
+                }
                 mPhysicalDisplayTokens[info->id] = new BBinder();
                 DisplayDeviceState state;
                 state.displayId = info->id;
@@ -2510,7 +2366,7 @@
         // display is used to calculate the hint, otherwise we use the
         // default display.
         //
-        // NOTE: we do this here, rather than in rebuildLayerStacks() so that
+        // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
         //
         // NOTE: layer transactions have taken place already, so we use their
@@ -2660,6 +2516,55 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
+void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+    if (mScheduler) {
+        // In practice it's not allowed to hotplug in/out the primary display once it's been
+        // connected during startup, but some tests do it, so just warn and return.
+        ALOGW("Can't re-init scheduler");
+        return;
+    }
+
+    int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
+                                                            getHwComposer().getConfigs(
+                                                                    primaryDisplayId),
+                                                            currentConfig);
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
+                                                          currentConfig, HWC_POWER_MODE_OFF);
+    mRefreshRateStats->setConfigMode(currentConfig);
+
+    // start the EventThread
+    mScheduler =
+            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+                                         *mRefreshRateConfigs);
+    mAppConnectionHandle =
+            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+                                         impl::EventThread::InterceptVSyncsCallback());
+    mSfConnectionHandle =
+            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
+                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+                                         [this](nsecs_t timestamp) {
+                                             mInterceptor->saveVSyncEvent(timestamp);
+                                         });
+
+    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
+                            mPhaseOffsets->getCurrentOffsets());
+
+    mRegionSamplingThread =
+            new RegionSamplingThread(*this, *mScheduler,
+                                     RegionSamplingThread::EnvironmentTimingTunables());
+
+    mScheduler->setChangeRefreshRateCallback(
+            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+                Mutex::Autolock lock(mStateLock);
+                setRefreshRateTo(type, event);
+            });
+}
+
 void SurfaceFlinger::commitTransaction()
 {
     withTracingLock([&]() {
@@ -2741,181 +2646,6 @@
     }
 }
 
-void SurfaceFlinger::computeVisibleRegions(
-        const sp<const DisplayDevice>& displayDevice, Region& outDirtyRegion,
-        Region& outOpaqueRegion,
-        std::vector<std::unique_ptr<compositionengine::OutputLayer>>& outLayersSortedByZ) {
-    ATRACE_CALL();
-    ALOGV("computeVisibleRegions");
-
-    auto display = displayDevice->getCompositionDisplay();
-
-    Region aboveOpaqueLayers;
-    Region aboveCoveredLayers;
-    Region dirty;
-
-    outDirtyRegion.clear();
-
-    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        auto compositionLayer = layer->getCompositionLayer();
-        if (compositionLayer == nullptr) {
-            return;
-        }
-
-        // Note: Converts a wp<LayerFE> to a sp<LayerFE>
-        auto layerFE = compositionLayer->getLayerFE();
-        if (layerFE == nullptr) {
-            return;
-        }
-
-        // Request a snapshot of the subset of state relevant to visibility
-        // determination
-        layerFE->latchCompositionState(compositionLayer->editState().frontEnd,
-                                       compositionengine::LayerFE::StateSubset::BasicGeometry);
-
-        // Work with a read-only copy of the snapshot
-        const auto& layerFEState = compositionLayer->getState().frontEnd;
-
-        // only consider the layers on the given layer stack
-        if (!display->belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly)) {
-            return;
-        }
-
-        /*
-         * opaqueRegion: area of a surface that is fully opaque.
-         */
-        Region opaqueRegion;
-
-        /*
-         * visibleRegion: area of a surface that is visible on screen
-         * and not fully transparent. This is essentially the layer's
-         * footprint minus the opaque regions above it.
-         * Areas covered by a translucent surface are considered visible.
-         */
-        Region visibleRegion;
-
-        /*
-         * coveredRegion: area of a surface that is covered by all
-         * visible regions above it (which includes the translucent areas).
-         */
-        Region coveredRegion;
-
-        /*
-         * transparentRegion: area of a surface that is hinted to be completely
-         * transparent. This is only used to tell when the layer has no visible
-         * non-transparent regions and can be removed from the layer list. It
-         * does not affect the visibleRegion of this layer or any layers
-         * beneath it. The hint may not be correct if apps don't respect the
-         * SurfaceView restrictions (which, sadly, some don't).
-         */
-        Region transparentRegion;
-
-        // handle hidden surfaces by setting the visible region to empty
-        if (CC_LIKELY(layerFEState.isVisible)) {
-            // Get the visible region
-            visibleRegion.set(
-                    Rect(layerFEState.geomLayerTransform.transform(layerFEState.geomLayerBounds)));
-            const ui::Transform& tr = layerFEState.geomLayerTransform;
-            if (!visibleRegion.isEmpty()) {
-                // Remove the transparent area from the visible region
-                if (!layerFEState.isOpaque) {
-                    if (tr.preserveRects()) {
-                        // transform the transparent region
-                        transparentRegion = tr.transform(layerFEState.transparentRegionHint);
-                    } else {
-                        // transformation too complex, can't do the
-                        // transparent region optimization.
-                        transparentRegion.clear();
-                    }
-                }
-
-                // compute the opaque region
-                const int32_t layerOrientation = tr.getOrientation();
-                if (layerFEState.isOpaque &&
-                    ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
-                    // the opaque region is the layer's footprint
-                    opaqueRegion = visibleRegion;
-                }
-            }
-        }
-
-        if (visibleRegion.isEmpty()) {
-            return;
-        }
-
-        // Clip the covered region to the visible region
-        coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
-
-        // Update aboveCoveredLayers for next (lower) layer
-        aboveCoveredLayers.orSelf(visibleRegion);
-
-        // subtract the opaque region covered by the layers above us
-        visibleRegion.subtractSelf(aboveOpaqueLayers);
-
-        //  Get coverage information for the layer as previously displayed
-        auto prevOutputLayer = display->getOutputLayerForLayer(compositionLayer.get());
-        // TODO(b/121291683): Define this as a constant in Region.h
-        const Region kEmptyRegion;
-        const Region& oldVisibleRegion =
-                prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
-        const Region& oldCoveredRegion =
-                prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
-
-        // compute this layer's dirty region
-        if (layerFEState.contentDirty) {
-            // we need to invalidate the whole region
-            dirty = visibleRegion;
-            // as well, as the old visible region
-            dirty.orSelf(oldVisibleRegion);
-        } else {
-            /* compute the exposed region:
-             *   the exposed region consists of two components:
-             *   1) what's VISIBLE now and was COVERED before
-             *   2) what's EXPOSED now less what was EXPOSED before
-             *
-             * note that (1) is conservative, we start with the whole
-             * visible region but only keep what used to be covered by
-             * something -- which mean it may have been exposed.
-             *
-             * (2) handles areas that were not covered by anything but got
-             * exposed because of a resize.
-             */
-            const Region newExposed = visibleRegion - coveredRegion;
-            const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
-            dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
-        }
-        dirty.subtractSelf(aboveOpaqueLayers);
-
-        // accumulate to the screen dirty region
-        outDirtyRegion.orSelf(dirty);
-
-        // Update aboveOpaqueLayers for next (lower) layer
-        aboveOpaqueLayers.orSelf(opaqueRegion);
-
-        // Compute the visible non-transparent region
-        Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
-
-        // Setup an output layer for this output if the layer is visible on this
-        // output
-        const auto& displayState = display->getState();
-        Region drawRegion(displayState.transform.transform(visibleNonTransparentRegion));
-        drawRegion.andSelf(displayState.bounds);
-        if (drawRegion.isEmpty()) {
-            return;
-        }
-
-        outLayersSortedByZ.emplace_back(display->getOrCreateOutputLayer(compositionLayer, layerFE));
-        auto& outputLayerState = outLayersSortedByZ.back()->editState();
-        outputLayerState.visibleRegion = std::move(visibleRegion);
-        outputLayerState.visibleNonTransparentRegion = std::move(visibleNonTransparentRegion);
-        outputLayerState.coveredRegion = std::move(coveredRegion);
-        outputLayerState.outputSpaceVisibleRegion = displayState.transform.transform(
-                outputLayerState.visibleRegion.intersect(displayState.viewport));
-    });
-
-    outOpaqueRegion = aboveOpaqueLayers;
-}
-
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
@@ -3108,9 +2838,9 @@
                 transactions.push_back(transaction);
                 applyTransactionState(transaction.states, transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.buffer, transaction.callback,
-                                      transaction.postTime, transaction.privileged,
-                                      /*isMainThread*/ true);
+                                      transaction.buffer, transaction.postTime,
+                                      transaction.privileged, transaction.hasListenerCallbacks,
+                                      transaction.listenerCallbacks, /*isMainThread*/ true);
                 transactionQueue.pop();
                 flushedATransaction = true;
             }
@@ -3157,13 +2887,11 @@
     return true;
 }
 
-void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
-                                         const Vector<DisplayState>& displays, uint32_t flags,
-                                         const sp<IBinder>& applyToken,
-                                         const InputWindowCommands& inputWindowCommands,
-                                         int64_t desiredPresentTime,
-                                         const client_cache_t& uncacheBuffer,
-                                         const std::vector<ListenerCallbacks>& listenerCallbacks) {
+void SurfaceFlinger::setTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+        int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks) {
     ATRACE_CALL();
 
     const int64_t postTime = systemTime();
@@ -3193,24 +2921,23 @@
     if (itr != mTransactionQueues.end() || !transactionIsReadyToBeApplied(
             desiredPresentTime, false /* useCachedExpectedPresentTime */, states)) {
         mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
-                                               uncacheBuffer, listenerCallbacks, postTime,
-                                               privileged);
+                                               uncacheBuffer, postTime, privileged,
+                                               hasListenerCallbacks, listenerCallbacks);
         setTransactionFlags(eTransactionFlushNeeded);
         return;
     }
 
     applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
-                          uncacheBuffer, listenerCallbacks, postTime, privileged);
+                          uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                          listenerCallbacks);
 }
 
-void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
-                                           const Vector<DisplayState>& displays, uint32_t flags,
-                                           const InputWindowCommands& inputWindowCommands,
-                                           const int64_t desiredPresentTime,
-                                           const client_cache_t& uncacheBuffer,
-                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                           const int64_t postTime, bool privileged,
-                                           bool isMainThread) {
+void SurfaceFlinger::applyTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+        const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3233,28 +2960,27 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
-    // In case the client has sent a Transaction that should receive callbacks but without any
-    // SurfaceControls that should be included in the callback, send the listener and callbackIds
-    // to the callback thread so it can send an empty callback
-    if (!listenerCallbacks.empty()) {
-        mTransactionCompletedThread.run();
-    }
-    for (const auto& listenerCallback : listenerCallbacks) {
-        mTransactionCompletedThread.startRegistration(listenerCallback);
+    // start and end registration for listeners w/ no surface so they can get their callback.  Note
+    // that listeners with SurfaceControls will start registration during setClientStateLocked
+    // below.
+    for (const auto& listener : listenerCallbacks) {
+        mTransactionCompletedThread.startRegistration(listener);
+        mTransactionCompletedThread.endRegistration(listener);
     }
 
+    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
-                                                 postTime, privileged);
+        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
+                                                 listenerCallbacksWithSurfaces);
     }
 
-    for (const auto& listenerCallback : listenerCallbacks) {
+    for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
         mTransactionCompletedThread.endRegistration(listenerCallback);
     }
 
     // If the state doesn't require a traversal and there are callbacks, send them now
-    if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) {
+    if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
         mTransactionCompletedThread.sendCallbacks();
     }
     transactionFlags |= clientStateFlags;
@@ -3382,17 +3108,23 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(
-        const ComposerState& composerState, int64_t desiredPresentTime,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-        bool privileged) {
+        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+        bool privileged,
+        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
 
+    for (auto& listener : s.listeners) {
+        // note that startRegistration will not re-register if the listener has
+        // already be registered for a prior surface control
+        mTransactionCompletedThread.startRegistration(listener);
+        listenerCallbacks.insert(listener);
+    }
+
     sp<Layer> layer(fromHandle(s.surface));
     if (layer == nullptr) {
-        for (auto& listenerCallback : listenerCallbacks) {
+        for (auto& [listener, callbackIds] : s.listeners) {
             mTransactionCompletedThread.registerUnpresentedCallbackHandle(
-                    new CallbackHandle(listenerCallback.transactionCompletedListener,
-                                       listenerCallback.callbackIds, s.surface));
+                    new CallbackHandle(listener, callbackIds, s.surface));
         }
         return 0;
     }
@@ -3615,8 +3347,8 @@
         }
     }
     std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
-        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
+        for (auto& [listener, callbackIds] : s.listeners) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
@@ -3898,7 +3630,8 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
+    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
+                        {});
 
     setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
 
@@ -4004,7 +3737,7 @@
 
     if (display->isPrimary()) {
         mTimeStats->setPowerMode(mode);
-        mRefreshRateStats.setPowerMode(mode);
+        mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
     }
 
@@ -4168,9 +3901,11 @@
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
     mScheduler->dump(result);
+    StringAppendF(&result, "+  Refresh rate switching: %s\n",
+                  mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
     StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
 
-    mRefreshRateStats.dump(result);
+    mRefreshRateStats->dump(result);
     result.append("\n");
 
     mPhaseOffsets->dump(result);
@@ -4179,10 +3914,9 @@
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
     StringAppendF(&result, "Allowed Display Configs: ");
-    for (const auto& [type, rate] : mRefreshRateConfigs.getRefreshRates()) {
-        if (rate && isDisplayConfigAllowed(rate->configId)) {
-            StringAppendF(&result, "%" PRIu32 " Hz, ", rate->fps);
-        }
+    for (int32_t configId : mAllowedDisplayConfigs) {
+        StringAppendF(&result, "%" PRIu32 " Hz, ",
+                      mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
     }
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -5024,7 +4758,8 @@
             case 1034: {
                 // TODO(b/129297325): expose this via developer menu option
                 n = data.readInt32();
-                if (n && !mRefreshRateOverlay) {
+                if (n && !mRefreshRateOverlay &&
+                    mRefreshRateConfigs->refreshRateSwitchingSupported()) {
                     RefreshRateType type;
                     {
                         std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -5610,25 +5345,6 @@
     }
 }
 
-void SurfaceFlinger::setPreferredDisplayConfig() {
-    const auto& type = mScheduler->getPreferredRefreshRateType();
-    const auto& config = mRefreshRateConfigs.getRefreshRate(type);
-    if (config && isDisplayConfigAllowed(config->configId)) {
-        ALOGV("switching to Scheduler preferred config %d", config->configId);
-        setDesiredActiveConfig({type, config->configId, Scheduler::ConfigEvent::Changed});
-    } else {
-        // Set the highest allowed config by iterating backwards on available refresh rates
-        const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
-        for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
-            if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
-                ALOGV("switching to allowed config %d", iter->second->configId);
-                setDesiredActiveConfig({iter->first, iter->second->configId,
-                        Scheduler::ConfigEvent::Changed});
-            }
-        }
-    }
-}
-
 void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
                                                       const std::vector<int32_t>& allowedConfigs) {
     if (!display->isPrimary()) {
@@ -5650,7 +5366,29 @@
     mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
                                 display->getActiveConfig());
 
-    setPreferredDisplayConfig();
+    if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+        const auto& type = mScheduler->getPreferredRefreshRateType();
+        const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type);
+        if (isDisplayConfigAllowed(config.configId)) {
+            ALOGV("switching to Scheduler preferred config %d", config.configId);
+            setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed});
+        } else {
+            // Set the highest allowed config by iterating backwards on available refresh rates
+            const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
+            for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+                if (isDisplayConfigAllowed(iter->second.configId)) {
+                    ALOGV("switching to allowed config %d", iter->second.configId);
+                    setDesiredActiveConfig(
+                            {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
+                    break;
+                }
+            }
+        }
+    } else if (!allowedConfigs.empty()) {
+        ALOGV("switching to config %d", allowedConfigs[0]);
+        setDesiredActiveConfig(
+                {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
+    }
 }
 
 status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index beb43d0..8c9b546 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,7 +89,6 @@
 class HWComposer;
 class IGraphicBufferProducer;
 class IInputFlinger;
-class InjectVSyncSource;
 class Layer;
 class MessageBase;
 class RefreshRateOverlay;
@@ -400,6 +399,7 @@
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                             bool hasListenerCallbacks,
                              const std::vector<ListenerCallbacks>& listenerCallbacks) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
@@ -529,9 +529,6 @@
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
-    // Query the Scheduler or allowed display configs list for a matching config, and set it
-    void setPreferredDisplayConfig() REQUIRES(mStateLock);
-
     // called on the main thread in response to setAllowedDisplayConfigs()
     void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
                                           const std::vector<int32_t>& allowedConfigs)
@@ -554,6 +551,7 @@
     void executeInputWindowCommands();
     void setInputWindowsFinished();
     void updateCursorAsync();
+    void initScheduler(DisplayId primaryDisplayId);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -568,10 +566,10 @@
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
-                               const client_cache_t& uncacheBuffer,
+                               const client_cache_t& uncacheBuffer, const int64_t postTime,
+                               bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               const int64_t postTime, bool privileged, bool isMainThread = false)
-            REQUIRES(mStateLock);
+                               bool isMainThread = false) REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
@@ -586,9 +584,11 @@
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                        bool useCachedExpectedPresentTime,
                                        const Vector<ComposerState>& states);
-    uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
-                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                  int64_t postTime, bool privileged) REQUIRES(mStateLock);
+    uint32_t setClientStateLocked(
+            const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+            bool privileged,
+            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+            REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -745,9 +745,6 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    void computeVisibleRegions(
-            const sp<const DisplayDevice>& display, Region& dirtyRegion, Region& opaqueRegion,
-            std::vector<std::unique_ptr<compositionengine::OutputLayer>>& outputLayers);
 
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
@@ -755,7 +752,6 @@
                                 std::shared_ptr<FenceTime>& presentFenceTime);
     void setCompositorTimingSnapped(const DisplayStatInfo& stats,
                                     nsecs_t compositeToPresentLatency);
-    void rebuildLayerStacks();
 
     void postFrame();
 
@@ -783,7 +779,7 @@
 
     bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock);
 
-    bool previousFrameMissed();
+    bool previousFrameMissed(int graceTimeMs = 0);
 
     // Populates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
@@ -934,8 +930,6 @@
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
     bool mGpuToCpuSupported = false;
-    std::unique_ptr<EventThread> mInjectorEventThread;
-    std::unique_ptr<InjectVSyncSource> mVSyncInjector;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -1025,25 +1019,27 @@
         TransactionState(const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                          int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                         const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-                         bool privileged)
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         std::vector<ListenerCallbacks> listenerCallbacks)
               : states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
                 buffer(uncacheBuffer),
-                callback(listenerCallbacks),
                 postTime(postTime),
-                privileged(privileged) {}
+                privileged(privileged),
+                hasListenerCallbacks(hasListenerCallbacks),
+                listenerCallbacks(listenerCallbacks) {}
 
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
         client_cache_t buffer;
-        std::vector<ListenerCallbacks> callback;
         const int64_t postTime;
         bool privileged;
+        bool hasListenerCallbacks;
+        std::vector<ListenerCallbacks> listenerCallbacks;
     };
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
 
@@ -1051,8 +1047,6 @@
      * Feature prototyping
      */
 
-    bool mInjectVSyncs = false;
-
     // Static screen stats
     bool mHasPoweredOff = false;
 
@@ -1102,8 +1096,8 @@
     // Optional to defer construction until scheduler connections are created.
     std::optional<scheduler::VSyncModulator> mVSyncModulator;
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-    scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 041ff8d..4ddc132 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -35,7 +35,6 @@
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
-#include "TimeStats/TimeStats.h"
 
 namespace android::surfaceflinger {
 
@@ -123,10 +122,6 @@
         sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
             return new ColorLayer(args);
         }
-
-        std::shared_ptr<TimeStats> createTimeStats() override {
-            return std::make_shared<android::impl::TimeStats>();
-        }
     };
     static Factory factory;
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 5d487e6..4f303a3 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -44,7 +44,6 @@
 class StartPropertySetThread;
 class SurfaceFlinger;
 class SurfaceInterceptor;
-class TimeStats;
 
 struct DisplayDeviceCreationArgs;
 struct LayerCreationArgs;
@@ -94,8 +93,6 @@
     virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
 
-    virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
-
 protected:
     ~Factory() = default;
 };
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 768074a..b4716eb 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -226,6 +226,14 @@
     return static_cast<int64_t>(defaultValue);
 }
 
+bool refresh_rate_switching(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 int32_t set_idle_timer_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5f88322..e394cca 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -73,6 +73,8 @@
 int64_t color_space_agnostic_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
+bool refresh_rate_switching(bool defaultValue);
+
 int32_t set_idle_timer_ms(int32_t defaultValue);
 
 int32_t set_touch_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index b01fa81..3df8360 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -34,6 +34,14 @@
 
 namespace impl {
 
+TimeStats::TimeStats() {
+    // Temporarily enable TimeStats by default. Telemetry is disabled while
+    // we move onto statsd, so TimeStats is currently not exercised at all
+    // during testing.
+    // TODO: remove this.
+    enable();
+}
+
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 9ebc1ad..1313132 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -106,7 +106,7 @@
     };
 
 public:
-    TimeStats() = default;
+    TimeStats();
 
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 1324f20..92e59d7 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -75,27 +75,29 @@
 }
 
 status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+    // begin running if not already running
+    run();
     std::lock_guard lock(mMutex);
     if (!mRunning) {
         ALOGE("cannot add callback because the callback thread isn't running");
         return BAD_VALUE;
     }
 
+    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
     auto& [listener, callbackIds] = listenerCallbacks;
 
-    if (mCompletedTransactions.count(listener) == 0) {
-        status_t err = listener->linkToDeath(mDeathRecipient);
-        if (err != NO_ERROR) {
-            ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
-            return err;
+    if (inserted) {
+        if (mCompletedTransactions.count(listener) == 0) {
+            status_t err = listener->linkToDeath(mDeathRecipient);
+            if (err != NO_ERROR) {
+                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+                return err;
+            }
         }
+        auto& transactionStatsDeque = mCompletedTransactions[listener];
+        transactionStatsDeque.emplace_back(callbackIds);
     }
 
-    mRegisteringTransactions.insert(listenerCallbacks);
-
-    auto& transactionStatsDeque = mCompletedTransactions[listener];
-    transactionStatsDeque.emplace_back(callbackIds);
-
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 56ab4e3..51b20cb 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -15,7 +15,7 @@
 module: "android.sysprop.SurfaceFlingerProperties"
 owner: Platform
 
-# The following two propertiess define (respectively):
+# The following two properties define (respectively):
 #
 # - The phase offset between hardware vsync and when apps are woken up by the
 #   Choreographer callback
@@ -301,9 +301,18 @@
     prop_name: "ro.surface_flinger.display_primary_white"
 }
 
-# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
-# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
-# refresh rate. Setting this property to 0 means there is no timer.
+# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
+# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
+# below that are related to refresh rate switching will only have an effect if
+# refresh_rate_switching is enabled.
+prop {
+    api_name: "refresh_rate_switching"
+    type: Boolean
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+}
+
 prop {
     api_name: "set_idle_timer_ms"
     type: Integer
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index b66e56e..2d52507 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -73,6 +73,10 @@
     enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
   }
   prop {
+    api_name: "refresh_rate_switching"
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+  }
+  prop {
     api_name: "running_without_sync_framework"
     prop_name: "ro.surface_flinger.running_without_sync_framework"
   }
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 159c2a4..7f960f3 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -43,7 +43,48 @@
         "libui",
         "libutils",
     ]
+}
 
+cc_defaults {
+    name: "ipc_defaults",
+    cflags: [
+        "-Wall",
+	"-Werror",
+    ],
+}
+
+cc_test {
+    name: "IPC_test",
+    defaults: ["ipc_defaults"],
+    test_suites: ["device-tests"],
+    srcs: [
+        "BufferGenerator.cpp",
+        "IPC_test.cpp",
+    ],
+    cppflags: [
+        "-Wall",
+	"-Werror",
+	"-Wformat",
+	"-Wthread-safety",
+	"-Wunused",
+	"-Wunreachable-code",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libbinder",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libgui",
+        "liblayers_proto",
+        "liblog",
+        "libprotobuf-cpp-full",
+        "libtimestats_proto",
+        "libui",
+        "libutils",
+    ],
+    cpp_std: "experimental",
+    gnu_extensions: false,
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
new file mode 100644
index 0000000..8a756a6
--- /dev/null
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <limits>
+
+#include <ui/DisplayInfo.h>
+
+#include <utils/String8.h>
+
+#include "BufferGenerator.h"
+#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using CallbackInfo = SurfaceComposerClient::CallbackInfo;
+using TCLHash = SurfaceComposerClient::TCLHash;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class TransactionHelper : public Transaction {
+public:
+    size_t getNumListeners() { return mListenerCallbacks.size(); }
+
+    std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+    getListenerCallbacks() {
+        return mListenerCallbacks;
+    }
+};
+
+class IPCTestUtils {
+public:
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                bool finalState = false);
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+};
+
+class IIPCTest : public IInterface {
+public:
+    DECLARE_META_INTERFACE(IPCTest)
+    enum class Tag : uint32_t {
+        SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+        InitClient,
+        CreateTransaction,
+        MergeAndApply,
+        VerifyCallbacks,
+        CleanUp,
+        Last,
+    };
+
+    virtual status_t setDeathToken(sp<IBinder>& token) = 0;
+
+    virtual status_t initClient() = 0;
+
+    virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width,
+                                       uint32_t height) = 0;
+
+    virtual status_t mergeAndApply(TransactionHelper transaction) = 0;
+
+    virtual status_t verifyCallbacks() = 0;
+
+    virtual status_t cleanUp() = 0;
+};
+
+class BpIPCTest : public SafeBpInterface<IIPCTest> {
+public:
+    explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) {
+        return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token);
+    }
+
+    status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction,
+                                                                  transaction, width, height);
+    }
+
+    status_t mergeAndApply(TransactionHelper transaction) {
+        return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction);
+    }
+
+    status_t verifyCallbacks() {
+        return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks);
+    }
+
+    status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); }
+};
+
+IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest")
+
+class onTestDeath : public IBinder::DeathRecipient {
+public:
+    void binderDied(const wp<IBinder>& /*who*/) override {
+        ALOGE("onTestDeath::binderDied, exiting");
+        exit(0);
+    }
+};
+
+sp<onTestDeath> getDeathToken() {
+    static sp<onTestDeath> token = new onTestDeath;
+    return token;
+}
+
+class BnIPCTest : public SafeBnInterface<IIPCTest> {
+public:
+    BnIPCTest() : SafeBnInterface("BnIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) override {
+        return token->linkToDeath(getDeathToken());
+    }
+
+    status_t initClient() override {
+        mClient = new SurfaceComposerClient;
+        auto err = mClient->initCheck();
+        return err;
+    }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        if (transaction == nullptr) {
+            ALOGE("Error in createTransaction: transaction is nullptr");
+            return BAD_VALUE;
+        }
+        mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0,
+                                                 PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 /*parent*/ nullptr);
+        sp<GraphicBuffer> gb;
+        sp<Fence> fence;
+        int err = IPCTestUtils::getBuffer(&gb, &fence);
+        if (err != NO_ERROR) return err;
+
+        TransactionUtils::fillGraphicBufferColor(gb,
+                                                 {0, 0, static_cast<int32_t>(width),
+                                                  static_cast<int32_t>(height)},
+                                                 Color::RED);
+        transaction->setLayerStack(mSurfaceControl, 0)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .setFrame(mSurfaceControl, Rect(0, 0, width, height))
+                .setBuffer(mSurfaceControl, gb)
+                .setAcquireFence(mSurfaceControl, fence)
+                .show(mSurfaceControl)
+                .addTransactionCompletedCallback(mCallbackHelper.function,
+                                                 mCallbackHelper.getContext());
+        return NO_ERROR;
+    }
+
+    status_t mergeAndApply(TransactionHelper /*transaction*/) {
+        // transaction.apply();
+        return NO_ERROR;
+    }
+
+    status_t verifyCallbacks() {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl);
+        EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true));
+        return NO_ERROR;
+    }
+
+    status_t cleanUp() {
+        if (mClient) mClient->dispose();
+        mSurfaceControl = nullptr;
+        IPCThreadState::self()->stopProcess();
+        return NO_ERROR;
+    }
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t /*flags*/) override {
+        EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+        EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last));
+        switch (static_cast<IIPCTest::Tag>(code)) {
+            case IIPCTest::Tag::SetDeathToken:
+                return callLocal(data, reply, &IIPCTest::setDeathToken);
+            case IIPCTest::Tag::InitClient:
+                return callLocal(data, reply, &IIPCTest::initClient);
+            case IIPCTest::Tag::CreateTransaction:
+                return callLocal(data, reply, &IIPCTest::createTransaction);
+            case IIPCTest::Tag::MergeAndApply:
+                return callLocal(data, reply, &IIPCTest::mergeAndApply);
+            case IIPCTest::Tag::VerifyCallbacks:
+                return callLocal(data, reply, &IIPCTest::verifyCallbacks);
+            case IIPCTest::Tag::CleanUp:
+                return callLocal(data, reply, &IIPCTest::cleanUp);
+            default:
+                return UNKNOWN_ERROR;
+        }
+    }
+
+private:
+    sp<SurfaceComposerClient> mClient;
+    sp<SurfaceControl> mSurfaceControl;
+    CallbackHelper mCallbackHelper;
+};
+
+class IPCTest : public ::testing::Test {
+public:
+    IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) {
+        ProcessState::self()->startThreadPool();
+    }
+    void SetUp() {
+        mClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        DisplayInfo info;
+        mClient->getDisplayInfo(mPrimaryDisplay, &info);
+        mDisplayWidth = info.w;
+        mDisplayHeight = info.h;
+
+        Transaction setupTransaction;
+        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+        setupTransaction.apply();
+    }
+
+protected:
+    sp<IIPCTest> initRemoteService();
+
+    sp<IBinder> mDeathRecipient;
+    sp<IIPCTest> mRemote;
+    sp<SurfaceComposerClient> mClient;
+    sp<IBinder> mPrimaryDisplay;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+    sp<SurfaceControl> sc;
+};
+
+status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    static BufferGenerator bufferGenerator;
+    return bufferGenerator.get(outBuffer, outFence);
+}
+
+void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                   bool finalState) {
+    CallbackData callbackData;
+    ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+    EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+    if (finalState) {
+        ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+    }
+}
+
+sp<IIPCTest> IPCTest::initRemoteService() {
+    static std::mutex mMutex;
+    static sp<IIPCTest> remote;
+    const String16 serviceName("IPCTest");
+
+    std::unique_lock<decltype(mMutex)> lock;
+    if (remote == nullptr) {
+        pid_t forkPid = fork();
+        EXPECT_NE(forkPid, -1);
+
+        if (forkPid == 0) {
+            sp<IIPCTest> nativeService = new BnIPCTest;
+            if (!nativeService) {
+                ALOGE("null service...");
+            }
+            status_t err = defaultServiceManager()->addService(serviceName,
+                                                               IInterface::asBinder(nativeService));
+            if (err != NO_ERROR) {
+                ALOGE("failed to add service: %d", err);
+            }
+            ProcessState::self()->startThreadPool();
+            IPCThreadState::self()->joinThreadPool();
+            [&]() { exit(0); }();
+        }
+        sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+        remote = interface_cast<IIPCTest>(binder);
+        remote->setDeathToken(mDeathRecipient);
+    }
+    return remote;
+}
+
+TEST_F(IPCTest, MergeBasic) {
+    CallbackHelper helper1;
+    sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                /*parent*/ nullptr);
+    sp<GraphicBuffer> gb;
+    sp<Fence> fence;
+    int err = IPCTestUtils::getBuffer(&gb, &fence);
+    ASSERT_EQ(NO_ERROR, err);
+    TransactionUtils::fillGraphicBufferColor(gb,
+                                             {0, 0, static_cast<int32_t>(mDisplayWidth),
+                                              static_cast<int32_t>(mDisplayHeight)},
+                                             Color::RED);
+
+    Transaction transaction;
+    transaction.setLayerStack(sc, 0)
+            .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
+            .setBuffer(sc, gb)
+            .setAcquireFence(sc, fence)
+            .show(sc)
+            .addTransactionCompletedCallback(helper1.function, helper1.getContext());
+
+    TransactionHelper remote;
+    mRemote->initClient();
+    mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2);
+    ASSERT_EQ(1, remote.getNumListeners());
+    auto remoteListenerCallbacks = remote.getListenerCallbacks();
+    auto remoteCallback = remoteListenerCallbacks.begin();
+    auto remoteCallbackInfo = remoteCallback->second;
+    auto remoteListenerScs = remoteCallbackInfo.surfaceControls;
+    ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size());
+    ASSERT_EQ(1, remoteListenerScs.size());
+
+    sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin());
+    transaction.merge(std::move(remote));
+    transaction.apply();
+
+    sleep(1);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc);
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc);
+    EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true));
+
+    mRemote->verifyCallbacks();
+    mRemote->cleanUp();
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 3e29016..9d74761 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -22,8 +22,6 @@
         "libgui",
         "libhardware",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblayers_proto",
         "liblog",
         "libnativewindow",
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 51956ec..4d21468 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -167,7 +167,9 @@
     }
     // TODO: Try registering the mock as the default service instead.
     property_set("debug.sf.hwc_service_name", "mock");
-    // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+
+    // This allows tests/SF to register/load a HIDL service not listed in manifest files.
+    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
     property_set("debug.sf.treble_testing_override", "true");
 }
 
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 093bcf5..c949d7c 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -171,7 +171,7 @@
     mMockComposer = new MockComposerClient;
     sp<ComposerClient> client = new ComposerClient(mMockComposer);
     mFakeService = new FakeComposerService(client);
-    (void)mFakeService->registerAsService("mock");
+    ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
 
     android::hardware::ProcessState::self()->startThreadPool();
     android::ProcessState::self()->startThreadPool();
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index dbd9b84..2662f52 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -42,6 +42,8 @@
 
 class MockVSyncSource : public VSyncSource {
 public:
+    const char* getName() const override { return "test"; }
+
     MOCK_METHOD1(setVSyncEnabled, void(bool));
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
@@ -54,8 +56,7 @@
 protected:
     class MockEventThreadConnection : public EventThreadConnection {
     public:
-        MockEventThreadConnection(android::impl::EventThread* eventThread,
-                                  ResyncCallback&& resyncCallback,
+        MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
                                   ISurfaceComposer::ConfigChanged configChanged)
               : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
@@ -67,7 +68,7 @@
     EventThreadTest();
     ~EventThreadTest() override;
 
-    void createThread();
+    void createThread(std::unique_ptr<VSyncSource>);
     sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
                                                    ISurfaceComposer::ConfigChanged configChanged);
 
@@ -91,9 +92,9 @@
     AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
-    MockVSyncSource mVSyncSource;
+    MockVSyncSource* mVSyncSource;
     VSyncSource::Callback* mCallback = nullptr;
-    std::unique_ptr<android::impl::EventThread> mThread;
+    std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
 
@@ -102,16 +103,19 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+    auto vsyncSource = std::make_unique<MockVSyncSource>();
+    mVSyncSource = vsyncSource.get();
+
+    EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
             .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setCallback(_))
+    EXPECT_CALL(*mVSyncSource, setCallback(_))
             .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+    EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
             .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
 
-    createThread();
+    createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
                                    ISurfaceComposer::eConfigChangedDispatch);
 
@@ -129,11 +133,9 @@
     EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
 }
 
-void EventThreadTest::createThread() {
-    mThread =
-            std::make_unique<android::impl::EventThread>(&mVSyncSource,
-                                                         mInterceptVSyncCallRecorder.getInvocable(),
-                                                         "unit-test-event-thread");
+void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+    mThread = std::make_unique<impl::EventThread>(std::move(source),
+                                                  mInterceptVSyncCallRecorder.getInvocable());
 
     // EventThread should register itself as VSyncSource callback.
     mCallback = expectVSyncSetCallbackCallReceived();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5067fe8..f315a8a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -23,7 +23,6 @@
 
 #include "DisplayHardware/HWC2.h"
 #include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 
 using namespace std::chrono_literals;
 using testing::_;
@@ -50,9 +49,8 @@
         ASSERT_EQ(left.configId, right.configId);
         ASSERT_EQ(left.name, right.name);
         ASSERT_EQ(left.fps, right.fps);
+        ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
     }
-
-    RefreshRateConfigs mConfigs;
 };
 
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -71,101 +69,39 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    mConfigs.populate(displayConfigs);
-
-    // We always store a configuration for screen off.
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(1, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
-
-    RefreshRate expectedConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedConfig, *powerSavingRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+                                                 /*currentConfig=*/0);
+    ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
 }
 
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    mConfigs.populate(displayConfigs);
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
+                                                         {HWC2_CONFIG_ID_90, VSYNC_90}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+                                                 /*currentConfig=*/0);
 
-    const auto& rates = mConfigs.getRefreshRates();
+    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+    const auto& rates = refreshRateConfigs->getRefreshRateMap();
     ASSERT_EQ(2, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_NE(rates.end(), defaultRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(2, mConfigs.getRefreshRates().size());
-}
-
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config90.setVsyncPeriod(VSYNC_90);
-    displayConfigs.push_back(config90.build());
-    mConfigs.populate(displayConfigs);
-
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(3, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
     const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
     const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
-    ASSERT_NE(rates.end(), powerSavingRate);
     ASSERT_NE(rates.end(), defaultRate);
     ASSERT_NE(rates.end(), performanceRate);
 
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-    RefreshRate expectedPerformanceConfig =
-            RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
-    assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+    RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
+    assertRatesEqual(expectedDefaultConfig, defaultRate->second);
+    RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
+                                             HWC2_CONFIG_ID_90};
+    assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
 
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+    assertRatesEqual(expectedDefaultConfig,
+                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
     assertRatesEqual(expectedPerformanceConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
 }
 } // namespace
 } // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 411ec61..cec0b32 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -22,7 +22,6 @@
 #include <thread>
 
 #include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockTimeStats.h"
 
 using namespace std::chrono_literals;
@@ -42,9 +41,18 @@
     RefreshRateStatsTest();
     ~RefreshRateStatsTest();
 
+    void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
+        mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
+                /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+        mRefreshRateStats =
+                std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+                                                   /*currentConfig=*/0,
+                                                   /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+    }
+
     mock::TimeStats mTimeStats;
-    RefreshRateConfigs mRefreshRateConfigs;
-    RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -63,63 +71,46 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    mRefreshRateConfigs.populate(configs);
-
-    // There is one default config, so the refresh rates should have one item.
-    EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size());
-}
-
 TEST_F(RefreshRateStatsTest, oneConfigTest) {
-    auto display = new Hwc2::mock::Display();
-
-    auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config.setVsyncPeriod(VSYNC_90);
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    configs.push_back(config.build());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{CONFIG_ID_90, VSYNC_90}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(2, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_EQ(0, times["90fps"]);
     // Setting up tests on mobile harness can be flaky with time passing, so testing for
     // exact time changes can result in flaxy numbers. To avoid that remember old
     // numbers to make sure the correct values are increasing in the next test.
     int screenOff = times["ScreenOff"];
-    int ninety = times["90fps"];
 
     // Screen is off by default.
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(0, times["90fps"]);
+    EXPECT_EQ(0u, times.count("90fps"));
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
     // does not update refresh rates that come from the config.
     EXPECT_LT(screenOff, times["ScreenOff"]);
@@ -127,93 +118,75 @@
 }
 
 TEST_F(RefreshRateStatsTest, twoConfigsTest) {
-    auto display = new Hwc2::mock::Display();
-
-    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config90.setVsyncPeriod(VSYNC_90);
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    configs.push_back(config90.build());
-
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    configs.push_back(config60.build());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(3, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("60fps"));
-    EXPECT_EQ(0, times["60fps"]);
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_EQ(0, times["90fps"]);
     // Setting up tests on mobile harness can be flaky with time passing, so testing for
     // exact time changes can result in flaxy numbers. To avoid that remember old
     // numbers to make sure the correct values are increasing in the next test.
     int screenOff = times["ScreenOff"];
-    int sixty = times["60fps"];
-    int ninety = times["90fps"];
 
     // Screen is off by default.
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
     // When power mode is normal, time for configs updates.
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
-    EXPECT_LT(sixty, times["60fps"]);
+    ASSERT_EQ(1u, times.count("60fps"));
+    EXPECT_LT(0, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_LT(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_LT(sixty, times["60fps"]);
 
     // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
     // does not update refresh rates that come from the config.
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ebcb9d8..b4cc1e1 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -9,6 +9,7 @@
 
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
 #include "mock/MockEventThread.h"
 
@@ -36,8 +37,8 @@
     SchedulerTest();
     ~SchedulerTest() override;
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-    TestableScheduler mScheduler{mRefreshRateConfigs};
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<TestableScheduler> mScheduler;
 
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
@@ -49,6 +50,13 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
+    std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+                                                            /*currentConfig=*/0);
+
+    mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
+
     auto eventThread = std::make_unique<mock::EventThread>();
     mEventThread = eventThread.get();
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -60,7 +68,7 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
+    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
     EXPECT_TRUE(mConnectionHandle);
 }
 
@@ -80,61 +88,57 @@
 
     sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            connection = mScheduler.createDisplayEventConnection(handle, ResyncCallback(),
-                                                                 ISurfaceComposer::
-                                                                         eConfigChangedSuppress));
+            connection = mScheduler->createDisplayEventConnection(handle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
     EXPECT_FALSE(connection);
-    EXPECT_FALSE(mScheduler.getEventThread(handle));
-    EXPECT_FALSE(mScheduler.getEventConnection(handle));
+    EXPECT_FALSE(mScheduler->getEventConnection(handle));
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenAcquired(handle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenReleased(handle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
 
     std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.dump(handle, output));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
     EXPECT_TRUE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.setPhaseOffset(handle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
     sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            connection =
-                    mScheduler.createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
-                                                            ISurfaceComposer::
-                                                                    eConfigChangedSuppress));
+            connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
     ASSERT_EQ(mEventThreadConnection, connection);
-
-    EXPECT_TRUE(mScheduler.getEventThread(mConnectionHandle));
-    EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
     ASSERT_NO_FATAL_FAILURE(
-            mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+            mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenAcquired(mConnectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenReleased(mConnectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
 
     std::string output("dump");
     EXPECT_CALL(*mEventThread, dump(output)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.dump(mConnectionHandle, output));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
     EXPECT_FALSE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.setPhaseOffset(mConnectionHandle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 780b608..ae72467 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -37,7 +37,7 @@
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
-        return Scheduler::createConnection(std::move(eventThread), ResyncCallback());
+        return Scheduler::createConnection(std::move(eventThread));
     }
 
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9536dd1..b77f82a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -18,9 +18,9 @@
 
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 
 #include "BufferQueueLayer.h"
@@ -32,12 +32,12 @@
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
-#include "TimeStats/TimeStats.h"
 
 namespace android {
 
@@ -151,11 +151,6 @@
         return nullptr;
     }
 
-    std::shared_ptr<TimeStats> createTimeStats() override {
-        // TODO: Use test-fixture controlled factory
-        return std::make_shared<android::impl::TimeStats>();
-    }
-
     using CreateBufferQueueFunction =
             std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
                                sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -194,9 +189,19 @@
                         std::unique_ptr<EventControlThread> eventControlThread,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread) {
+        std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+        mFlinger->mRefreshRateConfigs =
+                std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
+                                                                configs, /*currentConfig=*/0);
+        mFlinger->mRefreshRateStats =
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs,
+                                                              *mFlinger->mTimeStats,
+                                                              /*currentConfig=*/0,
+                                                              /*powerMode=*/HWC_POWER_MODE_OFF);
+
         mScheduler =
                 new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
-                                      mFlinger->mRefreshRateConfigs);
+                                      *mFlinger->mRefreshRateConfigs);
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -230,7 +235,7 @@
     void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
-        layer->getCompositionLayer()->editState().frontEnd.sidebandStream = sidebandStream;
+        layer->getCompositionLayer()->editFEState().sidebandStream = sidebandStream;
     }
 
     void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) {
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index ffacbfe..dee2cae 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -209,6 +209,10 @@
     return distr(mRandomEngine);
 }
 
+TEST_F(TimeStatsTest, enabledByDefault) {
+    ASSERT_TRUE(mTimeStats->isEnabled());
+}
+
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 1b1c1a7..e781c0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,6 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
     MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 4e461f9..4df7b7c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -27,7 +27,6 @@
         "libfmq",
         "libhardware",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libsync",
         "libui",
@@ -115,7 +114,6 @@
         "libbinder",
         "liblog",
         "libhardware",
-        "libhwbinder",
         "libhidlbase",
         "libui",
         "libutils",
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 85ef475..5686891 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -46,6 +46,7 @@
         "-Wno-padded",
         "-Wno-switch-enum",
         "-Wno-undef",
+        "-Wno-format-pedantic",
 
         // Have clang emit complete debug_info.
         "-fstandalone-debug",
@@ -75,7 +76,7 @@
 
     header_libs: [
         "hwvulkan_headers",
-        "libnativeloader-dummy-headers",
+        "libnativeloader-headers",
         "vulkan_headers",
     ],
     export_header_lib_headers: ["vulkan_headers"],
@@ -88,7 +89,6 @@
         "libbase",
         "libdl_android",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libui",
         "libgraphicsenv",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9a670f6..b413ac9 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1313,5 +1313,16 @@
     return result;
 }
 
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+                                uint32_t submitCount,
+                                const VkSubmitInfo* pSubmits,
+                                VkFence fence) {
+    ATRACE_CALL();
+
+    const auto& data = GetData(queue);
+
+    return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 57c956d..f058c47 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -131,6 +131,7 @@
 VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
 VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
 // clang-format on
 
 template <typename DispatchableType>
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 3495861..d829e41 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -444,6 +444,13 @@
         nullptr,
     },
     {
+        "vkQueueSubmit",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit),
+        nullptr,
+    },
+    {
         "vkSetHdrMetadataEXT",
         ProcHook::DEVICE,
         ProcHook::EXT_hdr_metadata,
@@ -537,6 +544,7 @@
     INIT_PROC(true, dev, GetDeviceProcAddr);
     INIT_PROC(true, dev, DestroyDevice);
     INIT_PROC(true, dev, GetDeviceQueue);
+    INIT_PROC(true, dev, QueueSubmit);
     INIT_PROC(true, dev, CreateImage);
     INIT_PROC(true, dev, DestroyImage);
     INIT_PROC(true, dev, AllocateCommandBuffers);
@@ -544,9 +552,9 @@
     INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
     INIT_PROC(false, dev, GetDeviceQueue2);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
-    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
     // clang-format on
 
     return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 79f070c..fb2f257 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -84,6 +84,7 @@
     PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
     PFN_vkDestroyDevice DestroyDevice;
     PFN_vkGetDeviceQueue GetDeviceQueue;
+    PFN_vkQueueSubmit QueueSubmit;
     PFN_vkCreateImage CreateImage;
     PFN_vkDestroyImage DestroyImage;
     PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
@@ -91,9 +92,9 @@
     PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
+    PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
-    PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     // clang-format on
 };
 
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 7c9b0c0..b8d7d2b 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -16,10 +16,10 @@
 
 // WARNING: This file is generated. See ../README.md for instructions.
 
-#include "null_driver_gen.h"
-
 #include <algorithm>
 
+#include "null_driver_gen.h"
+
 using namespace null_driver;
 
 namespace {
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 70ef340..668dc7d 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -204,9 +204,9 @@
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
 VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
-VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 04d9f23..ef36f8c 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -91,6 +91,8 @@
     'vkGetInstanceProcAddr' : True,
     'vkGetDeviceProcAddr' : True,
 
+    'vkQueueSubmit' : True,
+
     # VK_KHR_swapchain->VK_ANDROID_native_buffer translation
     'vkCreateImage' : True,
     'vkDestroyImage' : True,
@@ -191,6 +193,8 @@
     'vkGetInstanceProcAddr' : True,
     'vkGetDeviceProcAddr' : True,
 
+    'vkQueueSubmit' : True,
+
     # VK_KHR_swapchain v69 requirement
     'vkBindImageMemory2' : True,
     'vkBindImageMemory2KHR' : True
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index d9f97e1..fe9dab4 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -127,7 +127,7 @@
       'VkCommandBuffer ' : 'Device'
   }
 
-  if len(paramDict[functionName])>0:
+  if len(paramDict[functionName]) > 0:
     return switchCase.get(paramDict[functionName][0][0], 'Global')
   return 'Global'
 
@@ -150,7 +150,7 @@
 def clang_off(f, indent):
   f.write (clang_off_spaces * indent + '// clang-format off\n')
 
-clang_off_spaces = ' '*4
+clang_off_spaces = ' ' * 4
 
 parametersList = []
 paramDict = {}
@@ -166,16 +166,13 @@
   vulkan_registry = os.path.join(os.path.dirname(__file__),'..','..','..','..','external','vulkan-headers','registry','vk.xml')
   tree = ET.parse(vulkan_registry)
   root = tree.getroot()
-  protoset = False
-  fnName = ""
-  fnType = ""
   for commands in root.iter('commands'):
     for command in commands:
       if command.tag == 'command':
-        if protoset == True:
-          paramDict[fnName] = parametersList.copy()
         parametersList.clear()
         protoset = False
+        fnName = ""
+        fnType = ""
         if command.get('alias') != None:
           alias = command.get('alias')
           fnName = command.get('name')
@@ -184,20 +181,20 @@
           paramDict[fnName] = paramDict[alias].copy()
           returnTypeDict[fnName] = returnTypeDict[alias]
         for params in command:
-          if(params.tag == 'param'):
+          if params.tag == 'param':
             paramtype = ""
-            if params.text!=None:
-              paramtype = params.text
+            if params.text != None and params.text.strip() != '':
+              paramtype = params.text.strip() + ' '
             typeval = params.find('type')
             paramtype = paramtype + typeval.text
-            if typeval.tail!=None:
-              paramtype = paramtype + typeval.tail
+            if typeval.tail != None:
+              paramtype += typeval.tail.strip() + ' '
             pname = params.find('name')
             paramname = pname.text
-            if pname.tail != None:
-              parametersList.append((paramtype,paramname,pname.tail))
+            if pname.tail != None and pname.tail.strip() != '':
+              parametersList.append((paramtype, paramname, pname.tail.strip()))
             else:
-              parametersList.append((paramtype,paramname))
+              parametersList.append((paramtype, paramname))
           if params.tag == 'proto':
             for c in params:
               if c.tag == 'type':
@@ -207,6 +204,8 @@
                 protoset = True
                 allCommandsList.append(fnName)
                 returnTypeDict[fnName] = fnType
+        if protoset == True:
+          paramDict[fnName] = parametersList.copy()
 
   for exts in root.iter('extensions'):
     for extension in exts:
@@ -214,7 +213,7 @@
       if extension.tag == 'extension':
         extname = extension.get('name')
         for req in extension:
-          if req.get('feature')!=None:
+          if req.get('feature') != None:
             apiversion = req.get('feature')
           for commands in req:
             if commands.tag == 'command':
@@ -224,19 +223,6 @@
                 if apiversion != "":
                   versionDict[commandname] = apiversion
 
-  # TODO(adsrini): http://b/136570819
-  extensionsDict['vkGetSwapchainGrallocUsage2ANDROID'] = 'VK_ANDROID_native_buffer'
-  allCommandsList.append('vkGetSwapchainGrallocUsage2ANDROID')
-  returnTypeDict['vkGetSwapchainGrallocUsage2ANDROID'] = 'VkResult'
-  paramDict['vkGetSwapchainGrallocUsage2ANDROID'] = [
-    ('VkDevice ', 'device'),
-    ('VkFormat ', 'format'),
-    ('VkImageUsageFlags ', 'imageUsage'),
-    ('VkSwapchainImageUsageFlagsANDROID ', 'swapchainImageUsage'),
-    ('uint64_t* ', 'grallocConsumerUsage'),
-    ('uint64_t* ', 'grallocProducerUsage')
-  ]
-
   for feature in root.iter('feature'):
     apiversion = feature.get('name')
     for req in feature: