Merge "Revert "Adds out parameters for crop rectangle and transform""
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 021fdb5..9a8ec32 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -126,7 +126,6 @@
     { "aidl",       "AIDL calls",               ATRACE_TAG_AIDL, { } },
     { "nnapi",      "NNAPI",                    ATRACE_TAG_NNAPI, { } },
     { "rro",        "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
-    { "sysprop",    "System Property",          ATRACE_TAG_SYSPROP, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { k_pdxServiceCategory, "PDX services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 37fc9a9..e3c4ede 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -266,7 +266,10 @@
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace
 
-on post-fs-data
+# Only create the tracing instance if persist.mm_events.enabled
+# Attempting to remove the tracing instance after it has been created
+# will likely fail with EBUSY as it would be in use by traced_probes.
+on post-fs-data && property:persist.mm_events.enabled=true
 # Create MM Events Tracing Instance for Kmem Activity Trigger
     mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system
     mkdir /sys/kernel/tracing/instances/mm_events 0755 system system
@@ -275,10 +278,18 @@
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb
     chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb
 
+# Set the default buffer size to the minimum
+    write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1
+    write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1
+
 # Read and enable tracing
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on
     chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on
 
+# Tracing disabled by default
+    write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0
+    write /sys/kernel/tracing/instances/mm_events/tracing_on 0
+
 # Read and truncate kernel trace
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace
     chmod 0666 /sys/kernel/tracing/instances/mm_events/trace
diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
new file mode 100644
index 0000000..8262bc2
--- /dev/null
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_cmds_cmd_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_cmds_cmd_license"],
+}
+
+cc_fuzz {
+    name: "cmd_fuzzer",
+    srcs: [
+        "cmd_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libcmd",
+        "libutils",
+        "liblog",
+        "libselinux",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/cmds/cmd/fuzzer/README.md b/cmds/cmd/fuzzer/README.md
new file mode 100644
index 0000000..db37ece
--- /dev/null
+++ b/cmds/cmd/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libcmd_fuzzer
+
+## Plugin Design Considerations
+The fuzzer plugin for libcmd is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libcmd supports the following parameters:
+1. In (parameter name: `in`)
+2. Out (parameter name: `out`)
+3. Err (parameter name: `err`)
+4. Run Mode (parameter name: `runMode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `in` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `out` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `err` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `runMode` | 1.`RunMode::kStandalone` 2. `RunMode::kLibrary` | Value chosen from valid values using FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the cmd module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build cmd_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) cmd_fuzzer
+```
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/cmd_fuzzer/cmd_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/cmds/cmd/fuzzer/cmd_fuzzer.cpp b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
new file mode 100644
index 0000000..ab514a1
--- /dev/null
+++ b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/TextOutput.h>
+#include <cmd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+using namespace android;
+
+class TestTextOutput : public TextOutput {
+public:
+    TestTextOutput() {}
+    virtual ~TestTextOutput() {}
+
+    virtual status_t print(const char* /*txt*/, size_t /*len*/) { return NO_ERROR; }
+    virtual void moveIndent(int /*delta*/) { return; }
+    virtual void pushBundle() { return; }
+    virtual void popBundle() { return; }
+};
+
+class CmdFuzzer {
+public:
+    void process(const uint8_t* data, size_t size);
+
+private:
+    FuzzedDataProvider* mFDP = nullptr;
+};
+
+void CmdFuzzer::process(const uint8_t* data, size_t size) {
+    mFDP = new FuzzedDataProvider(data, size);
+    vector<string> arguments;
+    if (mFDP->ConsumeBool()) {
+        if (mFDP->ConsumeBool()) {
+            arguments = {"-w", "media.aaudio"};
+        } else {
+            arguments = {"-l"};
+        }
+    } else {
+        while (mFDP->remaining_bytes() > 0) {
+            size_t sizestr = mFDP->ConsumeIntegralInRange<size_t>(1, mFDP->remaining_bytes());
+            string argument = mFDP->ConsumeBytesAsString(sizestr);
+            arguments.emplace_back(argument);
+        }
+    }
+    vector<string_view> argSV;
+    for (auto& argument : arguments) {
+        argSV.emplace_back(argument.c_str());
+    }
+    int32_t in = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t out = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t err = open("/dev/null", O_RDWR | O_CREAT);
+    TestTextOutput output;
+    TestTextOutput error;
+    RunMode runMode = mFDP->ConsumeBool() ? RunMode::kStandalone : RunMode::kLibrary;
+    cmdMain(argSV, output, error, in, out, err, runMode);
+    delete mFDP;
+    close(in);
+    close(out);
+    close(err);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    CmdFuzzer cmdFuzzer;
+    cmdFuzzer.process(data, size);
+    return 0;
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2d11b90..75505f3 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1237,7 +1237,7 @@
         std::string path(title);
         path.append(" - ").append(String8(service).c_str());
         size_t bytes_written = 0;
-        status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args);
+        status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
         if (status == OK) {
             dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);
             std::chrono::duration<double> elapsed_seconds;
@@ -1316,7 +1316,7 @@
             path.append("_HIGH");
         }
         path.append(kProtoExt);
-        status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args);
+        status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args);
         if (status == OK) {
             status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout);
             bool dumpTerminated = (status == OK);
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 83a52b8..f8fdaa0 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -62,13 +62,14 @@
             "usage: dumpsys\n"
             "         To dump all services.\n"
             "or:\n"
-            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | "
-            "--skip SERVICES "
+            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--dump] [--pid] [--thread] [--help | "
+            "-l | --skip SERVICES "
             "| SERVICE [ARGS]]\n"
             "         --help: shows this help\n"
             "         -l: only list services, do not dump them\n"
             "         -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
             "         -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
+            "         --dump: ask the service to dump itself (this is the default)\n"
             "         --pid: dump PID instead of usual dump\n"
             "         --proto: filter services that support dumping data in proto format. Dumps\n"
             "               will be in proto format.\n"
@@ -127,10 +128,11 @@
     bool showListOnly = false;
     bool skipServices = false;
     bool asProto = false;
-    Type type = Type::DUMP;
+    int dumpTypeFlags = 0;
     int timeoutArgMs = 10000;
     int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
     static struct option longOptions[] = {{"help", no_argument, 0, 0},
+                                          {"dump", no_argument, 0, 0},
                                           {"pid", no_argument, 0, 0},
                                           {"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
@@ -168,12 +170,14 @@
                     usage();
                     return -1;
                 }
+            } else if (!strcmp(longOptions[optionIndex].name, "dump")) {
+                dumpTypeFlags |= TYPE_DUMP;
             } else if (!strcmp(longOptions[optionIndex].name, "pid")) {
-                type = Type::PID;
+                dumpTypeFlags |= TYPE_PID;
             } else if (!strcmp(longOptions[optionIndex].name, "stability")) {
-                type = Type::STABILITY;
+                dumpTypeFlags |= TYPE_STABILITY;
             } else if (!strcmp(longOptions[optionIndex].name, "thread")) {
-                type = Type::THREAD;
+                dumpTypeFlags |= TYPE_THREAD;
             }
             break;
 
@@ -211,6 +215,10 @@
         }
     }
 
+    if (dumpTypeFlags == 0) {
+        dumpTypeFlags = TYPE_DUMP;
+    }
+
     for (int i = optind; i < argc; i++) {
         if (skipServices) {
             skippedServices.add(String16(argv[i]));
@@ -263,7 +271,7 @@
         const String16& serviceName = services[i];
         if (IsSkipped(skippedServices, serviceName)) continue;
 
-        if (startDumpThread(type, serviceName, args) == OK) {
+        if (startDumpThread(dumpTypeFlags, serviceName, args) == OK) {
             bool addSeparator = (N > 1);
             if (addSeparator) {
                 writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
@@ -330,18 +338,21 @@
     }
 }
 
-static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) {
+static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd, bool exclusive) {
      pid_t pid;
      status_t status = service->getDebugPid(&pid);
      if (status != OK) {
          return status;
      }
+     if (!exclusive) {
+        WriteStringToFd("Service host process PID: ", fd.get());
+     }
      WriteStringToFd(std::to_string(pid) + "\n", fd.get());
      return OK;
 }
 
 static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) {
-     WriteStringToFd(internal::Stability::debugToString(service) + "\n", fd);
+     WriteStringToFd("Stability: " + internal::Stability::debugToString(service) + "\n", fd);
      return OK;
 }
 
@@ -362,7 +373,14 @@
     return OK;
 }
 
-status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
+static void reportDumpError(const String16& serviceName, status_t error, const char* context) {
+    if (error == OK) return;
+
+    std::cerr << "Error with service '" << serviceName << "' while " << context << ": "
+              << statusToString(error) << std::endl;
+}
+
+status_t Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName,
                                   const Vector<String16>& args) {
     sp<IBinder> service = sm_->checkService(serviceName);
     if (service == nullptr) {
@@ -383,29 +401,23 @@
 
     // dump blocks until completion, so spawn a thread..
     activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
-        status_t err = 0;
-
-        switch (type) {
-        case Type::DUMP:
-            err = service->dump(remote_end.get(), args);
-            break;
-        case Type::PID:
-            err = dumpPidToFd(service, remote_end);
-            break;
-        case Type::STABILITY:
-            err = dumpStabilityToFd(service, remote_end);
-            break;
-        case Type::THREAD:
-            err = dumpThreadsToFd(service, remote_end);
-            break;
-        default:
-            std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
-            return;
+        if (dumpTypeFlags & TYPE_PID) {
+            status_t err = dumpPidToFd(service, remote_end, dumpTypeFlags == TYPE_PID);
+            reportDumpError(serviceName, err, "dumping PID");
+        }
+        if (dumpTypeFlags & TYPE_STABILITY) {
+            status_t err = dumpStabilityToFd(service, remote_end);
+            reportDumpError(serviceName, err, "dumping stability");
+        }
+        if (dumpTypeFlags & TYPE_THREAD) {
+            status_t err = dumpThreadsToFd(service, remote_end);
+            reportDumpError(serviceName, err, "dumping thread info");
         }
 
-        if (err != OK) {
-            std::cerr << "Error dumping service info status_t: " << statusToString(err) << " "
-                 << serviceName << std::endl;
+        // other types always act as a header, this is usually longer
+        if (dumpTypeFlags & TYPE_DUMP) {
+            status_t err = service->dump(remote_end.get(), args);
+            reportDumpError(serviceName, err, "dumping");
         }
     });
     return OK;
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 1b3ae6a..05c5d5e 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -51,24 +51,25 @@
      */
     static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags);
 
-    enum class Type {
-        DUMP,      // dump using `dump` function
-        PID,       // dump pid of server only
-        STABILITY, // dump stability information of server
-        THREAD,    // dump thread usage of server only
+    enum Type {
+        TYPE_DUMP = 0x1,      // dump using `dump` function
+        TYPE_PID = 0x2,       // dump pid of server only
+        TYPE_STABILITY = 0x4, // dump stability information of server
+        TYPE_THREAD = 0x8,    // dump thread usage of server only
     };
 
     /**
      * Starts a thread to connect to a service and get its dump output. The thread redirects
      * the output to a pipe. Thread must be stopped by a subsequent call to {@code
      * stopDumpThread}.
+     * @param dumpTypeFlags operations to perform
      * @param serviceName
      * @param args list of arguments to pass to service dump method.
      * @return {@code OK} thread is started successfully.
      *         {@code NAME_NOT_FOUND} service could not be found.
      *         {@code != OK} error
      */
-    status_t startDumpThread(Type type, const String16& serviceName,
+    status_t startDumpThread(int dumpTypeFlags, const String16& serviceName,
                              const Vector<String16>& args);
 
     /**
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 277f445..312f4d7 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -200,7 +200,7 @@
         CaptureStdout();
         CaptureStderr();
         dump_.setServiceArgs(args, supportsProto, priorityFlags);
-        status_t status = dump_.startDumpThread(Dumpsys::Type::DUMP, serviceName, args);
+        status_t status = dump_.startDumpThread(Dumpsys::TYPE_DUMP, serviceName, args);
         EXPECT_THAT(status, Eq(0));
         status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false,
                                  elapsedDuration, bytesWritten);
@@ -627,6 +627,28 @@
     AssertOutputFormat(format);
 }
 
+// Tests 'dumpsys --thread --stability'
+TEST_F(DumpsysTest, ListAllServicesWithMultipleOptions) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet");
+
+    CallMain({"--pid", "--stability"});
+    AssertRunningServices({"Locksmith", "Valet"});
+
+    AssertOutputContains(std::to_string(getpid()));
+    AssertOutputContains("stability");
+}
+
+// Tests 'dumpsys --pid --stability service_name'
+TEST_F(DumpsysTest, ListServiceWithMultipleOptions) {
+    ExpectCheckService("Locksmith");
+    CallMain({"--pid", "--stability", "Locksmith"});
+
+    AssertOutputContains(std::to_string(getpid()));
+    AssertOutputContains("stability");
+}
+
 TEST_F(DumpsysTest, GetBytesWritten) {
     const char* serviceName = "service2";
     const char* dumpContents = "dump1";
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index ec3bc61..c18d3f5 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -24,7 +24,7 @@
 cc_defaults {
     name: "idlcli-defaults",
     shared_libs: [
-        "android.hardware.vibrator-V2-ndk_platform",
+        "android.hardware.vibrator-V2-ndk",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 2722e21..ff73c94 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -28,6 +28,7 @@
 #include <sstream>
 
 #include <android-base/file.h>
+#include <android-base/hex.h>
 #include <android-base/logging.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl-hash/Hash.h>
@@ -691,8 +692,7 @@
             }
 
             auto&& hashArray = hashChain[hashIndex];
-            std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
-            entry->hash = Hash::hexString(hashVec);
+            entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
         });
         if (!hashRet.isOk()) {
             handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp
index 8e21975..1753343 100644
--- a/cmds/lshal/TableEntry.cpp
+++ b/cmds/lshal/TableEntry.cpp
@@ -18,6 +18,7 @@
 
 #include <map>
 
+#include <android-base/hex.h>
 #include <android-base/strings.h>
 #include <hidl-hash/Hash.h>
 #include <vintf/parse_string.h>
@@ -104,7 +105,8 @@
 }
 
 std::string TableEntry::isReleased() const {
-    static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);
+    static const std::string unreleased = android::base::HexString(Hash::kEmptyHash.data(),
+                                                                   Hash::kEmptyHash.size());
 
     if (hash.empty()) {
         return "?";
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 29d4409..a177027 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -52,6 +52,7 @@
         BackgroundBlurRadiusChange  background_blur_radius  = 20;
         ShadowRadiusChange          shadow_radius           = 21;
         BlurRegionsChange           blur_regions            = 22;
+        TrustedOverlayChange        trusted_overlay         = 23;
     }
 }
 
@@ -197,6 +198,10 @@
     required float radius = 1;
 }
 
+message TrustedOverlayChange {
+    required float is_trusted_overlay = 1;
+}
+
 message BlurRegionsChange {
     repeated BlurRegionChange blur_regions = 1;
 }
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 9d88ca6..c0cbad8 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -14,3 +14,11 @@
     src: "android.hardware.biometrics.face.xml",
     filename_from_src: true,
 }
+
+prebuilt_etc {
+    name: "android.software.app_compat_overrides.xml",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "android.software.app_compat_overrides.xml",
+    filename_from_src: true,
+}
diff --git a/data/etc/android.software.app_compat_overrides.xml b/data/etc/android.software.app_compat_overrides.xml
new file mode 100644
index 0000000..2f9726a
--- /dev/null
+++ b/data/etc/android.software.app_compat_overrides.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Feature for devices that are opted-in to receive per-app compatibility
+     overrides. -->
+<permissions>
+  <feature name="android.software.app_compat_overrides" />
+</permissions>
diff --git a/docs/Doxyfile b/docs/Doxyfile
index a1bd960..ea22337 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1638,6 +1638,7 @@
     "__attribute__(x)=" \
     __ANDROID__ \
     __BIONIC__ \
+    "U_IN_DOXYGEN=1" \ # Required by the ICU4C module only
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
diff --git a/include/android/input.h b/include/android/input.h
index bb98beb..f03facb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -169,6 +169,9 @@
 
     /** Drag event */
     AINPUT_EVENT_TYPE_DRAG = 5,
+
+    /** TouchMode event */
+    AINPUT_EVENT_TYPE_TOUCH_MODE = 6,
 };
 
 /**
diff --git a/include/input/Input.h b/include/input/Input.h
index cd9b486..2e42409 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -886,6 +886,25 @@
     float mX, mY;
 };
 
+/*
+ * Touch mode events.
+ */
+class TouchModeEvent : public InputEvent {
+public:
+    virtual ~TouchModeEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_TOUCH_MODE; }
+
+    inline bool isInTouchMode() const { return mIsInTouchMode; }
+
+    void initialize(int32_t id, bool isInTouchMode);
+
+    void initialize(const TouchModeEvent& from);
+
+protected:
+    bool mIsInTouchMode;
+};
+
 /**
  * Base class for verified events.
  * Do not create a VerifiedInputEvent explicitly.
@@ -950,6 +969,7 @@
     virtual FocusEvent* createFocusEvent() = 0;
     virtual CaptureEvent* createCaptureEvent() = 0;
     virtual DragEvent* createDragEvent() = 0;
+    virtual TouchModeEvent* createTouchModeEvent() = 0;
 };
 
 /*
@@ -966,6 +986,7 @@
     virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
     virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
     virtual DragEvent* createDragEvent() override { return &mDragEvent; }
+    virtual TouchModeEvent* createTouchModeEvent() override { return &mTouchModeEvent; }
 
 private:
     KeyEvent mKeyEvent;
@@ -973,6 +994,7 @@
     FocusEvent mFocusEvent;
     CaptureEvent mCaptureEvent;
     DragEvent mDragEvent;
+    TouchModeEvent mTouchModeEvent;
 };
 
 /*
@@ -988,6 +1010,7 @@
     virtual FocusEvent* createFocusEvent() override;
     virtual CaptureEvent* createCaptureEvent() override;
     virtual DragEvent* createDragEvent() override;
+    virtual TouchModeEvent* createTouchModeEvent() override;
 
     void recycle(InputEvent* event);
 
@@ -999,6 +1022,7 @@
     std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
     std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
     std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
+    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index a790b56..9a150eb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -72,6 +72,7 @@
         CAPTURE,
         DRAG,
         TIMELINE,
+        TOUCH_MODE,
     };
 
     struct Header {
@@ -206,6 +207,15 @@
 
             inline size_t size() const { return sizeof(Timeline); }
         } timeline;
+
+        struct TouchMode {
+            int32_t eventId;
+            // The following 2 fields take up 4 bytes total
+            bool isInTouchMode;
+            uint8_t empty[3];
+
+            inline size_t size() const { return sizeof(TouchMode); }
+        } touchMode;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -388,6 +398,15 @@
      */
     status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
 
+    /* Publishes a touch mode event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode);
+
     struct Finished {
         uint32_t seq;
         bool handled;
@@ -658,6 +677,7 @@
     static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
     static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
+    static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
new file mode 100644
index 0000000..5832bf4
--- /dev/null
+++ b/include/private/performance_hint_private.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
+#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+struct APerformanceHintManager;
+struct APerformanceHintSession;
+
+/**
+ * An opaque type representing a handle to a performance hint manager.
+ * It must be released after use.
+ *
+ * <p>To use:<ul>
+ *    <li>Obtain the performance hint manager instance by calling
+ *        {@link APerformanceHint_getManager} function.</li>
+ *    <li>Create an {@link APerformanceHintSession} with
+ *        {@link APerformanceHint_createSession}.</li>
+ *    <li>Get the preferred update rate in nanoseconds with
+ *        {@link APerformanceHint_getPreferredUpdateRateNanos}.</li>
+ */
+typedef struct APerformanceHintManager APerformanceHintManager;
+
+/**
+ * An opaque type representing a handle to a performance hint session.
+ * A session can only be acquired from a {@link APerformanceHintManager}
+ * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be
+ * freed with {@link APerformanceHint_closeSession} after use.
+ *
+ * A Session represents a group of threads with an inter-related workload such that hints for
+ * their performance should be considered as a unit. The threads in a given session should be
+ * long-life and not created or destroyed dynamically.
+ *
+ * <p>Each session is expected to have a periodic workload with a target duration for each
+ * cycle. The cycle duration is likely greater than the target work duration to allow other
+ * parts of the pipeline to run within the available budget. For example, a renderer thread may
+ * work at 60hz in order to produce frames at the display's frame but have a target work
+ * duration of only 6ms.</p>
+ *
+ * <p>After each cycle of work, the client is expected to use
+ * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to
+ * complete.</p>
+ *
+ * <p>To use:<ul>
+ *    <li>Update a sessions target duration for each cycle of work
+ *        with  {@link APerformanceHint_updateTargetWorkDuration}.</li>
+ *    <li>Report the actual duration for the last cycle of work with
+ *        {@link APerformanceHint_reportActualWorkDuration}.</li>
+ *    <li>Release the session instance with
+ *        {@link APerformanceHint_closeSession}.</li></ul></p>
+ */
+typedef struct APerformanceHintSession APerformanceHintSession;
+
+/**
+  * Acquire an instance of the performance hint manager.
+  *
+  * @return manager instance on success, nullptr on failure.
+  */
+APerformanceHintManager* APerformanceHint_getManager();
+
+/**
+ * Creates a session for the given set of threads and sets their initial target work
+ * duration.
+ * @param manager The performance hint manager instance.
+ * @param threadIds The list of threads to be associated with this session. They must be part of
+ *     this app's thread group.
+ * @param size the size of threadIds.
+ * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session.
+ *     This must be positive.
+ * @return manager instance on success, nullptr on failure.
+ */
+APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
+                                                        const int32_t* threadIds, size_t size,
+                                                        int64_t initialTargetWorkDurationNanos);
+
+/**
+ * Get preferred update rate information for this device.
+ *
+ * @param manager The performance hint manager instance.
+ * @return the preferred update rate supported by device software.
+ */
+int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager);
+
+/**
+ * Updates this session's target duration for each cycle of work.
+ *
+ * @param session The performance hint session instance to update.
+ * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive.
+ * @return 0 on success
+ *         EINVAL if targetDurationNanos is not positive.
+ *         EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
+                                               int64_t targetDurationNanos);
+
+/**
+ * Reports the actual duration for the last cycle of work.
+ *
+ * <p>The system will attempt to adjust the core placement of the threads within the thread
+ * group and/or the frequency of the core on which they are run to bring the actual duration
+ * close to the target duration.</p>
+ *
+ * @param session The performance hint session instance to update.
+ * @param actualDurationNanos how long the thread group took to complete its last task in
+ *     nanoseconds. This must be positive.
+ * @return 0 on success
+ *         EINVAL if actualDurationNanos is not positive.
+ *         EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
+                                               int64_t actualDurationNanos);
+
+/**
+ * Release the performance hint manager pointer acquired via
+ * {@link APerformanceHint_createSession}.
+ *
+ * @param session The performance hint session instance to release.
+ */
+void APerformanceHint_closeSession(APerformanceHintSession* session);
+
+/**
+ * For testing only.
+ */
+void APerformanceHint_setIHintManagerForTesting(void* iManager);
+
+__END_DECLS
+
+#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index b4271dc..c18b1ce 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -120,6 +120,7 @@
         "RpcSession.cpp",
         "RpcServer.cpp",
         "RpcState.cpp",
+        "RpcTransportRaw.cpp",
         "Static.cpp",
         "Stability.cpp",
         "Status.cpp",
@@ -144,9 +145,6 @@
             enabled: false,
         },
         host: {
-            static_libs: [
-                "libbase",
-            ],
             srcs: [
                 "ServiceManagerHost.cpp",
                 "UtilsHost.cpp",
@@ -187,6 +185,10 @@
         "libutils",
     ],
 
+    static_libs: [
+        "libbase",
+    ],
+
     header_libs: [
         "libbinder_headers",
         "libandroid_runtime_vm_headers",
@@ -285,7 +287,7 @@
     // Do not expand the visibility.
     visibility: [
         "//packages/modules/Virtualization/authfs:__subpackages__",
-        "//packages/modules/Virtualization/compos",
+        "//packages/modules/Virtualization/compos:__subpackages__",
         "//packages/modules/Virtualization/microdroid",
     ],
 }
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3099296..765e21c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -261,15 +261,15 @@
         if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
             using android::internal::Stability;
 
-            auto category = Stability::getCategory(this);
+            int16_t stability = Stability::getRepr(this);
             Stability::Level required = privateVendor ? Stability::VENDOR
                 : Stability::getLocalLevel();
 
-            if (CC_UNLIKELY(!Stability::check(category, required))) {
+            if (CC_UNLIKELY(!Stability::check(stability, required))) {
                 ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.",
-                    category.debugString().c_str(),
-                    String8(getInterfaceDescriptor()).c_str(),
-                    Stability::levelString(required).c_str());
+                      Stability::levelString(stability).c_str(),
+                      String8(getInterfaceDescriptor()).c_str(),
+                      Stability::levelString(required).c_str());
                 return BAD_TYPE;
             }
         }
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 8676955..e4ac4b4 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -26,22 +26,6 @@
 
 namespace android {
 
-std::string hexString(const void* bytes, size_t len) {
-    if (bytes == nullptr) return "<null>";
-
-    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
-    const char chars[] = "0123456789abcdef";
-    std::string result;
-    result.resize(len * 2);
-
-    for (size_t i = 0; i < len; i++) {
-        result[2 * i] = chars[bytes8[i] >> 4];
-        result[2 * i + 1] = chars[bytes8[i] & 0xf];
-    }
-
-    return result;
-}
-
 // ---------------------------------------------------------------------
 
 static const char indentStr[] =
diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h
index 7ca087e..262dfba 100644
--- a/libs/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -23,8 +23,6 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-std::string hexString(const void* data, size_t size);
-
 const char* stringForIndent(int32_t indentLevel);
 
 typedef void (*debugPrintFunc)(void* cookie, const char* txt);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 2175fd4..6e318ea 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -154,8 +154,7 @@
     return checkPermission(permission, pid, uid);
 }
 
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
-{
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) {
     static Mutex gPermissionControllerLock;
     static sp<IPermissionController> gPermissionController;
 
@@ -180,8 +179,10 @@
 
             // Is this a permission failure, or did the controller go away?
             if (IInterface::asBinder(pc)->isBinderAlive()) {
-                ALOGW("Permission failure: %s from uid=%d pid=%d",
-                        String8(permission).string(), uid, pid);
+                if (logPermissionFailure) {
+                    ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).string(),
+                          uid, pid);
+                }
                 return false;
             }
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a2ac96e..e623874 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -173,7 +173,7 @@
 status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)
 {
     internal::Stability::tryMarkCompilationUnit(binder.get());
-    int16_t rep = internal::Stability::getCategory(binder.get()).repr();
+    int16_t rep = internal::Stability::getRepr(binder.get());
     return writeInt32(rep);
 }
 
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
index 98dee9a..ffc94b9 100644
--- a/libs/binder/RpcAddress.cpp
+++ b/libs/binder/RpcAddress.cpp
@@ -16,6 +16,7 @@
 
 #include <binder/RpcAddress.h>
 
+#include <android-base/hex.h>
 #include <binder/Parcel.h>
 
 #include "Debug.h"
@@ -94,7 +95,7 @@
 }
 
 std::string RpcAddress::toString() const {
-    return hexString(mRawAddr.get(), sizeof(RpcWireAddress));
+    return base::HexString(mRawAddr.get(), sizeof(RpcWireAddress));
 }
 
 status_t RpcAddress::writeToParcel(Parcel* parcel) const {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index c6cf2c5..879e462 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -26,6 +26,7 @@
 #include <android-base/scopeguard.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
+#include <binder/RpcTransportRaw.h>
 #include <log/log.h>
 
 #include "RpcSocketAddress.h"
@@ -37,13 +38,17 @@
 using base::ScopeGuard;
 using base::unique_fd;
 
-RpcServer::RpcServer() {}
+RpcServer::RpcServer(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory)
+      : mRpcTransportCtxFactory(std::move(rpcTransportCtxFactory)) {}
 RpcServer::~RpcServer() {
     (void)shutdown();
 }
 
-sp<RpcServer> RpcServer::make() {
-    return sp<RpcServer>::make();
+sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+    // Default is without TLS.
+    if (rpcTransportCtxFactory == nullptr)
+        rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make();
+    return sp<RpcServer>::make(std::move(rpcTransportCtxFactory));
 }
 
 void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
@@ -61,14 +66,13 @@
     return setupSocketServer(VsockSocketAddress(kAnyCid, port));
 }
 
-bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
-    const char* kAddr = "127.0.0.1";
-
+bool RpcServer::setupInetServer(const char* address, unsigned int port,
+                                unsigned int* assignedPort) {
     if (assignedPort != nullptr) *assignedPort = 0;
-    auto aiStart = InetSocketAddress::getAddrInfo(kAddr, port);
+    auto aiStart = InetSocketAddress::getAddrInfo(address, port);
     if (aiStart == nullptr) return false;
     for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
-        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
+        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port);
         if (!setupSocketServer(socketAddress)) {
             continue;
         }
@@ -95,7 +99,7 @@
 
         return true;
     }
-    ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
+    ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", address,
           port);
     return false;
 }
@@ -110,6 +114,10 @@
     return mMaxThreads;
 }
 
+void RpcServer::setProtocolVersion(uint32_t version) {
+    mProtocolVersion = version;
+}
+
 void RpcServer::setRootObject(const sp<IBinder>& binder) {
     std::lock_guard<std::mutex> _l(mLock);
     mRootObjectWeak = mRootObject = binder;
@@ -150,6 +158,10 @@
         mJoinThreadRunning = true;
         mShutdownTrigger = RpcSession::FdTrigger::make();
         LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Cannot create join signaler");
+
+        mCtx = mRpcTransportCtxFactory->newServerCtx();
+        LOG_ALWAYS_FATAL_IF(mCtx == nullptr, "Unable to create RpcTransportCtx with %s sockets",
+                            mRpcTransportCtxFactory->toCString());
     }
 
     status_t status;
@@ -216,6 +228,7 @@
     LOG_RPC_DETAIL("Finished waiting on shutdown.");
 
     mShutdownTrigger = nullptr;
+    mCtx = nullptr;
     return true;
 }
 
@@ -241,17 +254,56 @@
     // mShutdownTrigger can only be cleared once connection threads have joined.
     // It must be set before this thread is started
     LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
+    LOG_ALWAYS_FATAL_IF(server->mCtx == nullptr);
+
+    status_t status = OK;
+
+    int clientFdForLog = clientFd.get();
+    auto client = server->mCtx->newTransport(std::move(clientFd));
+    if (client == nullptr) {
+        ALOGE("Dropping accept4()-ed socket because sslAccept fails");
+        status = DEAD_OBJECT;
+        // still need to cleanup before we can return
+    } else {
+        LOG_RPC_DETAIL("Created RpcTransport %p for client fd %d", client.get(), clientFdForLog);
+    }
 
     RpcConnectionHeader header;
-    status_t status = server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &header,
-                                                                       sizeof(header));
-    bool idValid = status == OK;
-    if (!idValid) {
-        ALOGE("Failed to read ID for client connecting to RPC server: %s",
-              statusToString(status).c_str());
-        // still need to cleanup before we can return
+    if (status == OK) {
+        status = server->mShutdownTrigger->interruptableReadFully(client.get(), &header,
+                                                                  sizeof(header));
+        if (status != OK) {
+            ALOGE("Failed to read ID for client connecting to RPC server: %s",
+                  statusToString(status).c_str());
+            // still need to cleanup before we can return
+        }
     }
-    bool incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+
+    bool incoming = false;
+    uint32_t protocolVersion = 0;
+    RpcAddress sessionId = RpcAddress::zero();
+    bool requestingNewSession = false;
+
+    if (status == OK) {
+        incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+        protocolVersion = std::min(header.version,
+                                   server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
+        sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
+        requestingNewSession = sessionId.isZero();
+
+        if (requestingNewSession) {
+            RpcNewSessionResponse response{
+                    .version = protocolVersion,
+            };
+
+            status = server->mShutdownTrigger->interruptableWriteFully(client.get(), &response,
+                                                                       sizeof(response));
+            if (status != OK) {
+                ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
+                // still need to cleanup before we can return
+            }
+        }
+    }
 
     std::thread thisThread;
     sp<RpcSession> session;
@@ -269,19 +321,16 @@
         };
         server->mConnectingThreads.erase(threadId);
 
-        if (!idValid || server->mShutdownTrigger->isTriggered()) {
+        if (status != OK || server->mShutdownTrigger->isTriggered()) {
             return;
         }
 
-        RpcAddress sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
-
-        if (sessionId.isZero()) {
+        if (requestingNewSession) {
             if (incoming) {
                 ALOGE("Cannot create a new session with an incoming connection, would leak");
                 return;
             }
 
-            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
@@ -295,6 +344,7 @@
 
             session = RpcSession::make();
             session->setMaxThreads(server->mMaxThreads);
+            if (!session->setProtocolVersion(protocolVersion)) return;
             if (!session->setForServer(server,
                                        sp<RpcServer::EventListener>::fromExisting(
                                                static_cast<RpcServer::EventListener*>(
@@ -316,7 +366,7 @@
         }
 
         if (incoming) {
-            LOG_ALWAYS_FATAL_IF(!session->addOutgoingConnection(std::move(clientFd), true),
+            LOG_ALWAYS_FATAL_IF(!session->addOutgoingConnection(std::move(client), true),
                                 "server state must already be initialized");
             return;
         }
@@ -325,7 +375,7 @@
         session->preJoinThreadOwnership(std::move(thisThread));
     }
 
-    auto setupResult = session->preJoinSetup(std::move(clientFd));
+    auto setupResult = session->preJoinSetup(std::move(client));
 
     // avoid strong cycle
     server = nullptr;
@@ -369,7 +419,7 @@
     return true;
 }
 
-void RpcServer::onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) {
+void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) {
     auto id = session->mId;
     LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
     LOG_RPC_DETAIL("Dropping session with address %s", id->toString().c_str());
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c01a03d..7565f1b 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -30,6 +30,7 @@
 #include <android_runtime/vm.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
+#include <binder/RpcTransportRaw.h>
 #include <binder/Stability.h>
 #include <jni.h>
 #include <utils/String8.h>
@@ -46,7 +47,8 @@
 
 using base::unique_fd;
 
-RpcSession::RpcSession() {
+RpcSession::RpcSession(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory)
+      : mRpcTransportCtxFactory(std::move(rpcTransportCtxFactory)) {
     LOG_RPC_DETAIL("RpcSession created %p", this);
 
     mState = std::make_unique<RpcState>();
@@ -59,8 +61,11 @@
                         "Should not be able to destroy a session with servers in use.");
 }
 
-sp<RpcSession> RpcSession::make() {
-    return sp<RpcSession>::make();
+sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+    // Default is without TLS.
+    if (rpcTransportCtxFactory == nullptr)
+        rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make();
+    return sp<RpcSession>::make(std::move(rpcTransportCtxFactory));
 }
 
 void RpcSession::setMaxThreads(size_t threads) {
@@ -77,6 +82,31 @@
     return mMaxThreads;
 }
 
+bool RpcSession::setProtocolVersion(uint32_t version) {
+    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+
+    std::lock_guard<std::mutex> _l(mMutex);
+    if (mProtocolVersion && version > *mProtocolVersion) {
+        ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u",
+              *mProtocolVersion, version);
+        return false;
+    }
+
+    mProtocolVersion = version;
+    return true;
+}
+
+std::optional<uint32_t> RpcSession::getProtocolVersion() {
+    std::lock_guard<std::mutex> _l(mMutex);
+    return mProtocolVersion;
+}
+
 bool RpcSession::setupUnixDomainClient(const char* path) {
     return setupSocketClient(UnixSocketAddress(path));
 }
@@ -96,7 +126,19 @@
     return false;
 }
 
+bool RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
+    return setupClient([&](const RpcAddress& sessionId, bool incoming) {
+        // std::move'd from fd becomes -1 (!ok())
+        if (!fd.ok()) {
+            fd = request();
+            if (!fd.ok()) return false;
+        }
+        return initAndAddConnection(std::move(fd), sessionId, incoming);
+    });
+}
+
 bool RpcSession::addNullDebuggingClient() {
+    // Note: only works on raw sockets.
     unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
 
     if (serverFd == -1) {
@@ -104,7 +146,17 @@
         return false;
     }
 
-    return addOutgoingConnection(std::move(serverFd), false);
+    auto ctx = mRpcTransportCtxFactory->newClientCtx();
+    if (ctx == nullptr) {
+        ALOGE("Unable to create RpcTransportCtx for null debugging client");
+        return false;
+    }
+    auto server = ctx->newTransport(std::move(serverFd));
+    if (server == nullptr) {
+        ALOGE("Unable to set up RpcTransport");
+        return false;
+    }
+    return addOutgoingConnection(std::move(server), false);
 }
 
 sp<IBinder> RpcSession::getRootObject() {
@@ -132,6 +184,7 @@
     if (wait) {
         LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
         mShutdownListener->waitForShutdown(_l);
+
         LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed");
     }
 
@@ -179,11 +232,13 @@
     return mWrite == -1;
 }
 
+status_t RpcSession::FdTrigger::triggerablePoll(RpcTransport* rpcTransport, int16_t event) {
+    return triggerablePoll(rpcTransport->pollSocket(), event);
+}
+
 status_t RpcSession::FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
     while (true) {
-        pollfd pfd[]{{.fd = fd.get(),
-                      .events = static_cast<int16_t>(event | POLLHUP),
-                      .revents = 0},
+        pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
                      {.fd = mRead.get(), .events = POLLHUP, .revents = 0}};
         int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
         if (ret < 0) {
@@ -199,28 +254,30 @@
     }
 }
 
-status_t RpcSession::FdTrigger::interruptableWriteFully(base::borrowed_fd fd, const void* data,
-                                                        size_t size) {
+status_t RpcSession::FdTrigger::interruptableWriteFully(RpcTransport* rpcTransport,
+                                                        const void* data, size_t size) {
     const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data);
     const uint8_t* end = buffer + size;
 
     MAYBE_WAIT_IN_FLAKE_MODE;
 
     status_t status;
-    while ((status = triggerablePoll(fd, POLLOUT)) == OK) {
-        ssize_t writeSize = TEMP_FAILURE_RETRY(send(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
-        if (writeSize == 0) return DEAD_OBJECT;
-
-        if (writeSize < 0) {
-            return -errno;
+    while ((status = triggerablePoll(rpcTransport, POLLOUT)) == OK) {
+        auto writeSize = rpcTransport->send(buffer, end - buffer);
+        if (!writeSize.ok()) {
+            LOG_RPC_DETAIL("RpcTransport::send(): %s", writeSize.error().message().c_str());
+            return writeSize.error().code() == 0 ? UNKNOWN_ERROR : -writeSize.error().code();
         }
-        buffer += writeSize;
+
+        if (*writeSize == 0) return DEAD_OBJECT;
+
+        buffer += *writeSize;
         if (buffer == end) return OK;
     }
     return status;
 }
 
-status_t RpcSession::FdTrigger::interruptableReadFully(base::borrowed_fd fd, void* data,
+status_t RpcSession::FdTrigger::interruptableReadFully(RpcTransport* rpcTransport, void* data,
                                                        size_t size) {
     uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
     uint8_t* end = buffer + size;
@@ -228,14 +285,16 @@
     MAYBE_WAIT_IN_FLAKE_MODE;
 
     status_t status;
-    while ((status = triggerablePoll(fd, POLLIN)) == OK) {
-        ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
-        if (readSize == 0) return DEAD_OBJECT; // EOF
-
-        if (readSize < 0) {
-            return -errno;
+    while ((status = triggerablePoll(rpcTransport, POLLIN)) == OK) {
+        auto readSize = rpcTransport->recv(buffer, end - buffer);
+        if (!readSize.ok()) {
+            LOG_RPC_DETAIL("RpcTransport::recv(): %s", readSize.error().message().c_str());
+            return readSize.error().code() == 0 ? UNKNOWN_ERROR : -readSize.error().code();
         }
-        buffer += readSize;
+
+        if (*readSize == 0) return DEAD_OBJECT; // EOF
+
+        buffer += *readSize;
         if (buffer == end) return OK;
     }
     return status;
@@ -261,7 +320,7 @@
     return OK;
 }
 
-void RpcSession::WaitForShutdownListener::onSessionLockedAllIncomingThreadsEnded(
+void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded(
         const sp<RpcSession>& session) {
     (void)session;
     mShutdown = true;
@@ -288,12 +347,19 @@
     }
 }
 
-RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(base::unique_fd fd) {
+RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(
+        std::unique_ptr<RpcTransport> rpcTransport) {
     // must be registered to allow arbitrary client code executing commands to
     // be able to do nested calls (we can't only read from it)
-    sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(fd));
+    sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(rpcTransport));
 
-    status_t status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+    status_t status;
+
+    if (connection == nullptr) {
+        status = DEAD_OBJECT;
+    } else {
+        status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this));
+    }
 
     return PreJoinSetupResult{
             .connection = std::move(connection),
@@ -360,6 +426,7 @@
     sp<RpcConnection>& connection = setupResult.connection;
 
     if (setupResult.status == OK) {
+        LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded");
         JavaThreadAttacher javaThreadAttacher;
         while (true) {
             status_t status = session->state()->getAndExecuteCommand(connection, session,
@@ -375,9 +442,6 @@
               statusToString(setupResult.status).c_str());
     }
 
-    LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
-                        "bad state: connection object guaranteed to be in list");
-
     sp<RpcSession::EventListener> listener;
     {
         std::lock_guard<std::mutex> _l(session->mMutex);
@@ -389,6 +453,12 @@
         listener = session->mEventListener.promote();
     }
 
+    // done after all cleanup, since session shutdown progresses via callbacks here
+    if (connection != nullptr) {
+        LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
+                            "bad state: connection object guaranteed to be in list");
+    }
+
     session = nullptr;
 
     if (listener != nullptr) {
@@ -405,7 +475,8 @@
     return server;
 }
 
-bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+bool RpcSession::setupClient(
+        const std::function<bool(const RpcAddress& sessionId, bool incoming)>& connectAndInit) {
     {
         std::lock_guard<std::mutex> _l(mMutex);
         LOG_ALWAYS_FATAL_IF(mOutgoingConnections.size() != 0,
@@ -413,44 +484,61 @@
                             mOutgoingConnections.size());
     }
 
-    if (!setupOneSocketConnection(addr, RpcAddress::zero(), false /*incoming*/)) return false;
+    if (!connectAndInit(RpcAddress::zero(), false /*incoming*/)) return false;
+
+    {
+        ExclusiveConnection connection;
+        status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
+                                                    ConnectionUse::CLIENT, &connection);
+        if (status != OK) return false;
+
+        uint32_t version;
+        status = state()->readNewSessionResponse(connection.get(),
+                                                 sp<RpcSession>::fromExisting(this), &version);
+        if (!setProtocolVersion(version)) return false;
+    }
 
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once.
     // TODO(b/186470974): first risk of blocking
     size_t numThreadsAvailable;
     if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
-        ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+        ALOGE("Could not get max threads after initial session setup: %s",
               statusToString(status).c_str());
         return false;
     }
 
     if (status_t status = readId(); status != OK) {
-        ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+        ALOGE("Could not get session id after initial session setup: %s",
               statusToString(status).c_str());
         return false;
     }
 
-    // we've already setup one client
-    for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
-        // TODO(b/189955605): shutdown existing connections?
-        if (!setupOneSocketConnection(addr, mId.value(), false /*incoming*/)) return false;
-    }
-
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once - the other side should be responsible for setting
     // up additional connections. We need to create at least one (unless 0 are
     // requested to be set) in order to allow the other side to reliably make
     // any requests at all.
 
+    // we've already setup one client
+    for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+        if (!connectAndInit(mId.value(), false /*incoming*/)) return false;
+    }
+
     for (size_t i = 0; i < mMaxThreads; i++) {
-        if (!setupOneSocketConnection(addr, mId.value(), true /*incoming*/)) return false;
+        if (!connectAndInit(mId.value(), true /*incoming*/)) return false;
     }
 
     return true;
 }
 
-bool RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, const RpcAddress& id,
+bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+    return setupClient([&](const RpcAddress& sessionId, bool incoming) {
+        return setupOneSocketConnection(addr, sessionId, incoming);
+    });
+}
+
+bool RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, const RpcAddress& sessionId,
                                           bool incoming) {
     for (size_t tries = 0; tries < 5; tries++) {
         if (tries > 0) usleep(10000);
@@ -474,33 +562,60 @@
                   strerror(savedErrno));
             return false;
         }
-
-        RpcConnectionHeader header{.options = 0};
-        memcpy(&header.sessionId, &id.viewRawEmbedded(), sizeof(RpcWireAddress));
-
-        if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
-
-        if (sizeof(header) != TEMP_FAILURE_RETRY(write(serverFd.get(), &header, sizeof(header)))) {
-            int savedErrno = errno;
-            ALOGE("Could not write connection header to socket at %s: %s", addr.toString().c_str(),
-                  strerror(savedErrno));
-            return false;
-        }
-
         LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
 
-        if (incoming) {
-            return addIncomingConnection(std::move(serverFd));
-        } else {
-            return addOutgoingConnection(std::move(serverFd), true);
-        }
+        return initAndAddConnection(std::move(serverFd), sessionId, incoming);
     }
 
     ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
     return false;
 }
 
-bool RpcSession::addIncomingConnection(unique_fd fd) {
+bool RpcSession::initAndAddConnection(unique_fd fd, const RpcAddress& sessionId, bool incoming) {
+    auto ctx = mRpcTransportCtxFactory->newClientCtx();
+    if (ctx == nullptr) {
+        ALOGE("Unable to create client RpcTransportCtx with %s sockets",
+              mRpcTransportCtxFactory->toCString());
+        return false;
+    }
+    auto server = ctx->newTransport(std::move(fd));
+    if (server == nullptr) {
+        ALOGE("Unable to set up RpcTransport in %s context", mRpcTransportCtxFactory->toCString());
+        return false;
+    }
+
+    LOG_RPC_DETAIL("Socket at client with RpcTransport %p", server.get());
+
+    RpcConnectionHeader header{
+            .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
+            .options = 0,
+    };
+    memcpy(&header.sessionId, &sessionId.viewRawEmbedded(), sizeof(RpcWireAddress));
+
+    if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
+
+    auto sentHeader = server->send(&header, sizeof(header));
+    if (!sentHeader.ok()) {
+        ALOGE("Could not write connection header to socket: %s",
+              sentHeader.error().message().c_str());
+        return false;
+    }
+    if (*sentHeader != sizeof(header)) {
+        ALOGE("Could not write connection header to socket: sent %zd bytes, expected %zd",
+              *sentHeader, sizeof(header));
+        return false;
+    }
+
+    LOG_RPC_DETAIL("Socket at client: header sent");
+
+    if (incoming) {
+        return addIncomingConnection(std::move(server));
+    } else {
+        return addOutgoingConnection(std::move(server), true /*init*/);
+    }
+}
+
+bool RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) {
     std::mutex mutex;
     std::condition_variable joinCv;
     std::unique_lock<std::mutex> lock(mutex);
@@ -509,13 +624,13 @@
     bool ownershipTransferred = false;
     thread = std::thread([&]() {
         std::unique_lock<std::mutex> threadLock(mutex);
-        unique_fd movedFd = std::move(fd);
+        std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport);
         // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
         sp<RpcSession> session = thiz;
         session->preJoinThreadOwnership(std::move(thread));
 
         // only continue once we have a response or the connection fails
-        auto setupResult = session->preJoinSetup(std::move(movedFd));
+        auto setupResult = session->preJoinSetup(std::move(movedRpcTransport));
 
         ownershipTransferred = true;
         threadLock.unlock();
@@ -529,7 +644,7 @@
     return true;
 }
 
-bool RpcSession::addOutgoingConnection(unique_fd fd, bool init) {
+bool RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) {
     sp<RpcConnection> connection = sp<RpcConnection>::make();
     {
         std::lock_guard<std::mutex> _l(mMutex);
@@ -542,7 +657,7 @@
             if (mShutdownTrigger == nullptr) return false;
         }
 
-        connection->fd = std::move(fd);
+        connection->rpcTransport = std::move(rpcTransport);
         connection->exclusiveTid = gettid();
         mOutgoingConnections.push_back(connection);
     }
@@ -577,26 +692,44 @@
     return true;
 }
 
-sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(unique_fd fd) {
+sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(
+        std::unique_ptr<RpcTransport> rpcTransport) {
     std::lock_guard<std::mutex> _l(mMutex);
+
+    if (mIncomingConnections.size() >= mMaxThreads) {
+        ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
+              mIncomingConnections.size(), mMaxThreads);
+        return nullptr;
+    }
+
+    // Don't accept any more connections, some have shutdown. Usually this
+    // happens when new connections are still being established as part of a
+    // very short-lived session which shuts down after it already started
+    // accepting new connections.
+    if (mIncomingConnections.size() < mMaxIncomingConnections) {
+        return nullptr;
+    }
+
     sp<RpcConnection> session = sp<RpcConnection>::make();
-    session->fd = std::move(fd);
+    session->rpcTransport = std::move(rpcTransport);
     session->exclusiveTid = gettid();
+
     mIncomingConnections.push_back(session);
+    mMaxIncomingConnections = mIncomingConnections.size();
 
     return session;
 }
 
 bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
-    std::lock_guard<std::mutex> _l(mMutex);
+    std::unique_lock<std::mutex> _l(mMutex);
     if (auto it = std::find(mIncomingConnections.begin(), mIncomingConnections.end(), connection);
         it != mIncomingConnections.end()) {
         mIncomingConnections.erase(it);
         if (mIncomingConnections.size() == 0) {
             sp<EventListener> listener = mEventListener.promote();
             if (listener) {
-                listener->onSessionLockedAllIncomingThreadsEnded(
-                        sp<RpcSession>::fromExisting(this));
+                _l.unlock();
+                listener->onSessionAllIncomingThreadsEnded(sp<RpcSession>::fromExisting(this));
             }
         }
         return true;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 332c75f..23382c3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,6 +18,7 @@
 
 #include "RpcState.h"
 
+#include <android-base/hex.h>
 #include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
@@ -272,8 +273,8 @@
 status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
                            const sp<RpcSession>& session, const char* what, const void* data,
                            size_t size) {
-    LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, connection->fd.get(),
-                   hexString(data, size).c_str());
+    LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+                   android::base::HexString(data, size).c_str());
 
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot send %s at size %zu (too big)", what, size);
@@ -281,11 +282,12 @@
         return BAD_VALUE;
     }
 
-    if (status_t status = session->mShutdownTrigger->interruptableWriteFully(connection->fd.get(),
-                                                                             data, size);
+    if (status_t status =
+                session->mShutdownTrigger->interruptableWriteFully(connection->rpcTransport.get(),
+                                                                   data, size);
         status != OK) {
-        LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on fd %d, error: %s", what, size,
-                       connection->fd.get(), statusToString(status).c_str());
+        LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+                       connection->rpcTransport.get(), statusToString(status).c_str());
         (void)session->shutdownAndWait(false);
         return status;
     }
@@ -303,15 +305,28 @@
     }
 
     if (status_t status =
-                session->mShutdownTrigger->interruptableReadFully(connection->fd.get(), data, size);
+                session->mShutdownTrigger->interruptableReadFully(connection->rpcTransport.get(),
+                                                                  data, size);
         status != OK) {
-        LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on fd %d, error: %s", what, size,
-                       connection->fd.get(), statusToString(status).c_str());
+        LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
+                       connection->rpcTransport.get(), statusToString(status).c_str());
         return status;
     }
 
-    LOG_RPC_DETAIL("Received %s on fd %d: %s", what, connection->fd.get(),
-                   hexString(data, size).c_str());
+    LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
+                   android::base::HexString(data, size).c_str());
+    return OK;
+}
+
+status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                          const sp<RpcSession>& session, uint32_t* version) {
+    RpcNewSessionResponse response;
+    if (status_t status =
+                rpcRec(connection, session, "new session response", &response, sizeof(response));
+        status != OK) {
+        return status;
+    }
+    *version = response.version;
     return OK;
 }
 
@@ -477,7 +492,8 @@
         return status;
 
     if (flags & IBinder::FLAG_ONEWAY) {
-        LOG_RPC_DETAIL("Oneway command, so no longer waiting on %d", connection->fd.get());
+        LOG_RPC_DETAIL("Oneway command, so no longer waiting on RpcTransport %p",
+                       connection->rpcTransport.get());
 
         // Do not wait on result.
         // However, too many oneway calls may cause refcounts to build up and fill up the socket,
@@ -572,7 +588,7 @@
 
 status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
                                         const sp<RpcSession>& session, CommandType type) {
-    LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", connection->fd.get());
+    LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
 
     RpcWireHeader command;
     if (status_t status = rpcRec(connection, session, "command header", &command, sizeof(command));
@@ -585,8 +601,7 @@
 status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection,
                                  const sp<RpcSession>& session, CommandType type) {
     uint8_t buf;
-    while (0 < TEMP_FAILURE_RETRY(
-                       recv(connection->fd.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT))) {
+    while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) {
         status_t status = getAndExecuteCommand(connection, session, type);
         if (status != OK) return status;
     }
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 5ac0b97..1446eec 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,6 +60,8 @@
     RpcState();
     ~RpcState();
 
+    status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                    const sp<RpcSession>& session, uint32_t* version);
     status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                 const sp<RpcSession>& session);
     status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
new file mode 100644
index 0000000..2fc1945
--- /dev/null
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcRawTransport"
+#include <log/log.h>
+
+#include <binder/RpcTransportRaw.h>
+
+#include "RpcState.h"
+
+using android::base::ErrnoError;
+using android::base::Result;
+
+namespace android {
+
+namespace {
+
+// RpcTransport with TLS disabled.
+class RpcTransportRaw : public RpcTransport {
+public:
+    explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+    Result<size_t> send(const void *buf, size_t size) override {
+        ssize_t ret = TEMP_FAILURE_RETRY(::send(mSocket.get(), buf, size, MSG_NOSIGNAL));
+        if (ret < 0) {
+            return ErrnoError() << "send()";
+        }
+        return ret;
+    }
+    Result<size_t> recv(void *buf, size_t size) override {
+        ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_NOSIGNAL));
+        if (ret < 0) {
+            return ErrnoError() << "recv()";
+        }
+        return ret;
+    }
+    Result<size_t> peek(void *buf, size_t size) override {
+        ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK | MSG_DONTWAIT));
+        if (ret < 0) {
+            return ErrnoError() << "recv(MSG_PEEK)";
+        }
+        return ret;
+    }
+    bool pending() override { return false; }
+    android::base::borrowed_fd pollSocket() const override { return mSocket; }
+
+private:
+    android::base::unique_fd mSocket;
+};
+
+// RpcTransportCtx with TLS disabled.
+class RpcTransportCtxRaw : public RpcTransportCtx {
+public:
+    std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd) const {
+        return std::make_unique<RpcTransportRaw>(std::move(fd));
+    }
+};
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newServerCtx() const {
+    return std::make_unique<RpcTransportCtxRaw>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newClientCtx() const {
+    return std::make_unique<RpcTransportCtxRaw>();
+}
+
+const char *RpcTransportCtxFactoryRaw::toCString() const {
+    return "raw";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryRaw::make() {
+    return std::unique_ptr<RpcTransportCtxFactoryRaw>(new RpcTransportCtxFactoryRaw());
+}
+
+} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 2a44c7a..0f8efd2 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -37,9 +37,20 @@
  * either as part of a new session or an existing session
  */
 struct RpcConnectionHeader {
+    uint32_t version; // maximum supported by caller
+    uint8_t reserver0[4];
     RpcWireAddress sessionId;
     uint8_t options;
-    uint8_t reserved[7];
+    uint8_t reserved1[7];
+};
+
+/**
+ * In response to an RpcConnectionHeader which corresponds to a new session,
+ * this returns information to the server.
+ */
+struct RpcNewSessionResponse {
+    uint32_t version; // maximum supported by callee <= maximum supported by caller
+    uint8_t reserved[4];
 };
 
 #define RPC_CONNECTION_INIT_OKAY "cci"
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 00b69d5..2d05fb2 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -23,20 +23,6 @@
 namespace android {
 namespace internal {
 
-// the libbinder parcel format is currently unstable
-
-// oldest version which is supported
-constexpr uint8_t kBinderWireFormatOldest = 1;
-// current version
-constexpr uint8_t kBinderWireFormatVersion = 1;
-
-Stability::Category Stability::Category::currentFromLevel(Level level) {
-    return {
-        .version = kBinderWireFormatVersion,
-        .level = level,
-    };
-}
-
 void Stability::forceDowngradeToStability(const sp<IBinder>& binder, Level level) {
     // Downgrading a remote binder would require also copying the version from
     // the binder sent here. In practice though, we don't need to downgrade the
@@ -44,8 +30,7 @@
     // what we can do to it.
     LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
 
-    auto stability = Category::currentFromLevel(level);
-    status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
+    status_t result = setRepr(binder.get(), level, REPR_LOG | REPR_ALLOW_DOWNGRADE);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
@@ -61,41 +46,31 @@
     forceDowngradeToStability(binder, Level::VENDOR);
 }
 
-std::string Stability::Category::debugString() {
-    return levelString(level) + " wire protocol version "
-        + std::to_string(version);
-}
-
 void Stability::markCompilationUnit(IBinder* binder) {
-    auto stability = Category::currentFromLevel(getLocalLevel());
-    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+    status_t result = setRepr(binder, getLocalLevel(), REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
 void Stability::markVintf(IBinder* binder) {
-    auto stability = Category::currentFromLevel(Level::VINTF);
-    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+    status_t result = setRepr(binder, Level::VINTF, REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
 std::string Stability::debugToString(const sp<IBinder>& binder) {
-    auto stability = getCategory(binder.get());
-    return stability.debugString();
+    return levelString(getRepr(binder.get()));
 }
 
 void Stability::markVndk(IBinder* binder) {
-    auto stability = Category::currentFromLevel(Level::VENDOR);
-    status_t result = setRepr(binder, stability.repr(), REPR_LOG);
+    status_t result = setRepr(binder, Level::VENDOR, REPR_LOG);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
 bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) {
-    return check(getCategory(binder.get()), Level::VINTF);
+    return check(getRepr(binder.get()), Level::VINTF);
 }
 
 void Stability::tryMarkCompilationUnit(IBinder* binder) {
-    auto stability = Category::currentFromLevel(getLocalLevel());
-    (void) setRepr(binder, stability.repr(), REPR_NONE);
+    (void)setRepr(binder, getLocalLevel(), REPR_NONE);
 }
 
 Stability::Level Stability::getLocalLevel() {
@@ -111,92 +86,77 @@
 #endif
 }
 
-status_t Stability::setRepr(IBinder* binder, int32_t representation, uint32_t flags) {
+status_t Stability::setRepr(IBinder* binder, int32_t setting, uint32_t flags) {
     bool log = flags & REPR_LOG;
     bool allowDowngrade = flags & REPR_ALLOW_DOWNGRADE;
 
-    auto current = getCategory(binder);
-    auto setting = Category::fromRepr(representation);
-
-    // If we have ahold of a binder with a newer declared version, then it
-    // should support older versions, and we will simply write our parcels with
-    // the current wire parcel format.
-    if (setting.version < kBinderWireFormatOldest) {
-        // always log, because this shouldn't happen
-        ALOGE("Cannot accept binder with older binder wire protocol version "
-              "%u. Versions less than %u are unsupported.", setting.version,
-               kBinderWireFormatOldest);
-        return BAD_TYPE;
-    }
+    int16_t current = getRepr(binder);
 
     // null binder is always written w/ 'UNDECLARED' stability
     if (binder == nullptr) {
-        if (setting.level == UNDECLARED) {
+        if (setting == UNDECLARED) {
             return OK;
         } else {
             if (log) {
-                ALOGE("Null binder written with stability %s.",
-                    levelString(setting.level).c_str());
+                ALOGE("Null binder written with stability %s.", levelString(setting).c_str());
             }
             return BAD_TYPE;
         }
     }
 
-    if (!isDeclaredLevel(setting.level)) {
+    if (!isDeclaredLevel(setting)) {
         if (log) {
-            ALOGE("Can only set known stability, not %u.", setting.level);
+            ALOGE("Can only set known stability, not %d.", setting);
         }
         return BAD_TYPE;
     }
+    Level levelSetting = static_cast<Level>(setting);
 
     if (current == setting) return OK;
 
-    bool hasAlreadyBeenSet = current.repr() != 0;
-    bool isAllowedDowngrade = allowDowngrade && check(current, setting.level);
+    bool hasAlreadyBeenSet = current != Level::UNDECLARED;
+    bool isAllowedDowngrade = allowDowngrade && check(current, levelSetting);
     if (hasAlreadyBeenSet && !isAllowedDowngrade) {
         if (log) {
             ALOGE("Interface being set with %s but it is already marked as %s",
-                  setting.debugString().c_str(),
-                  current.debugString().c_str());
+                  levelString(setting).c_str(), levelString(current).c_str());
         }
         return BAD_TYPE;
     }
 
     if (isAllowedDowngrade) {
-        ALOGI("Interface set with %s downgraded to %s stability",
-              current.debugString().c_str(),
-              setting.debugString().c_str());
+        ALOGI("Interface set with %s downgraded to %s stability", levelString(current).c_str(),
+              levelString(setting).c_str());
     }
 
     BBinder* local = binder->localBinder();
     if (local != nullptr) {
-        local->mStability = setting.repr();
+        local->mStability = setting;
     } else {
-        binder->remoteBinder()->mStability = setting.repr();
+        binder->remoteBinder()->mStability = setting;
     }
 
     return OK;
 }
 
-Stability::Category Stability::getCategory(IBinder* binder) {
+int16_t Stability::getRepr(IBinder* binder) {
     if (binder == nullptr) {
-        return Category::currentFromLevel(Level::UNDECLARED);
+        return Level::UNDECLARED;
     }
 
     BBinder* local = binder->localBinder();
     if (local != nullptr) {
-        return Category::fromRepr(local->mStability);
+        return local->mStability;
     }
 
-    return Category::fromRepr(binder->remoteBinder()->mStability);
+    return binder->remoteBinder()->mStability;
 }
 
-bool Stability::check(Category provided, Level required) {
-    bool stable = (provided.level & required) == required;
+bool Stability::check(int16_t provided, Level required) {
+    bool stable = (provided & required) == required;
 
-    if (provided.level != UNDECLARED && !isDeclaredLevel(provided.level)) {
-        ALOGE("Unknown stability when checking interface stability %d.",
-              provided.level);
+    if (provided != UNDECLARED && !isDeclaredLevel(provided)) {
+        ALOGE("Unknown stability when checking interface stability %d.", provided);
 
         stable = false;
     }
@@ -204,11 +164,11 @@
     return stable;
 }
 
-bool Stability::isDeclaredLevel(Level stability) {
+bool Stability::isDeclaredLevel(int32_t stability) {
     return stability == VENDOR || stability == SYSTEM || stability == VINTF;
 }
 
-std::string Stability::levelString(Level level) {
+std::string Stability::levelString(int32_t level) {
     switch (level) {
         case Level::UNDECLARED: return "undeclared stability";
         case Level::VENDOR: return "vendor stability";
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index e524dab..d121ce2 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -111,15 +111,15 @@
     errWrite.reset();
     ret.pid = pid;
 
-    auto handlePoll = [](android::base::unique_fd* fd, const pollfd& pfd, std::string* s) {
+    auto handlePoll = [](android::base::unique_fd* fd, const pollfd* pfd, std::string* s) {
         if (!fd->ok()) return true;
-        if (pfd.revents & POLLIN) {
+        if (pfd->revents & POLLIN) {
             char buf[1024];
             ssize_t n = TEMP_FAILURE_RETRY(read(fd->get(), buf, sizeof(buf)));
             if (n < 0) return false;
             if (n > 0) *s += std::string_view(buf, n);
         }
-        if (pfd.revents & POLLHUP) {
+        if (pfd->revents & POLLHUP) {
             fd->reset();
         }
         return true;
@@ -142,9 +142,9 @@
         int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
         if (pollRet == -1) return android::base::ErrnoError() << "poll()";
 
-        if (!handlePoll(&ret.outPipe, *outPollFd, &ret.stdout))
+        if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdout))
             return android::base::ErrnoError() << "read(stdout)";
-        if (!handlePoll(&ret.errPipe, *errPollFd, &ret.stderr))
+        if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderr))
             return android::base::ErrnoError() << "read(stderr)";
 
         if (end && end(ret)) return ret;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 8e46147..d152005 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -167,7 +167,8 @@
 bool checkCallingPermission(const String16& permission);
 bool checkCallingPermission(const String16& permission,
                             int32_t* outPid, int32_t* outUid);
-bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid,
+                     bool logPermissionFailure = true);
 
 #ifndef __ANDROID__
 // Create an IServiceManager that delegates the service manager on the device via adb.
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index c8d2857..4abf3b9 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -19,6 +19,7 @@
 #include <binder/IBinder.h>
 #include <binder/RpcAddress.h>
 #include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
@@ -47,7 +48,8 @@
  */
 class RpcServer final : public virtual RefBase, private RpcSession::EventListener {
 public:
-    static sp<RpcServer> make();
+    static sp<RpcServer> make(
+            std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     /**
      * This represents a session for responses, e.g.:
@@ -72,8 +74,14 @@
      * Set |port| to 0 to pick an ephemeral port; see discussion of
      * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
      * will be set to the picked port number, if it is not null.
+     *
+     * Set the IPv4 address for the socket to be listening on.
+     * "127.0.0.1" allows for local connections from the same device.
+     * "0.0.0.0" allows for connections on any IP address that the device may
+     * have
      */
-    [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+    [[nodiscard]] bool setupInetServer(const char* address, unsigned int port,
+                                       unsigned int* assignedPort);
 
     /**
      * If setup*Server has been successful, return true. Otherwise return false.
@@ -105,6 +113,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the latest protocol version which is supported by a client is
+     * used. However, this can be used in order to prevent newer protocol
+     * versions from ever being used. This is expected to be useful for testing.
+     */
+    void setProtocolVersion(uint32_t version);
+
+    /**
      * The root object can be retrieved by any client, without any
      * authentication. TODO(b/183988761)
      *
@@ -154,16 +169,18 @@
 
 private:
     friend sp<RpcServer>;
-    RpcServer();
+    explicit RpcServer(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
 
-    void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
+    void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
     void onSessionIncomingThreadEnded() override;
 
     static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd);
     bool setupSocketServer(const RpcSocketAddress& address);
 
+    const std::unique_ptr<RpcTransportCtxFactory> mRpcTransportCtxFactory;
     bool mAgreedExperimental = false;
     size_t mMaxThreads = 1;
+    std::optional<uint32_t> mProtocolVersion;
     base::unique_fd mServer; // socket we are accepting sessions on
 
     std::mutex mLock; // for below
@@ -175,6 +192,7 @@
     std::map<RpcAddress, sp<RpcSession>> mSessions;
     std::unique_ptr<RpcSession::FdTrigger> mShutdownTrigger;
     std::condition_variable mShutdownCv;
+    std::unique_ptr<RpcTransportCtx> mCtx;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index fdca2a9..0b46606 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -18,6 +18,8 @@
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
 #include <binder/RpcAddress.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
@@ -36,6 +38,11 @@
 class RpcServer;
 class RpcSocketAddress;
 class RpcState;
+class RpcTransport;
+
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 0;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
 
 /**
  * This represents a session (group of connections) between a client
@@ -44,7 +51,8 @@
  */
 class RpcSession final : public virtual RefBase {
 public:
-    static sp<RpcSession> make();
+    static sp<RpcSession> make(
+            std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     /**
      * Set the maximum number of threads allowed to be made (for things like callbacks).
@@ -60,6 +68,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the minimum of the supported versions of the client and the
+     * server will be used. Usually, this API should only be used for debugging.
+     */
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
+    std::optional<uint32_t> getProtocolVersion();
+
+    /**
      * This should be called once per thread, matching 'join' in the remote
      * process.
      */
@@ -76,6 +91,19 @@
     [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
 
     /**
+     * Starts talking to an RPC server which has already been connected to. This
+     * is expected to be used when another process has permission to connect to
+     * a binder RPC service, but this process only has permission to talk to
+     * that service.
+     *
+     * For convenience, if 'fd' is -1, 'request' will be called.
+     *
+     * For future compatibility, 'request' should not reference any stack data.
+     */
+    [[nodiscard]] bool setupPreconnectedClient(base::unique_fd fd,
+                                               std::function<base::unique_fd()>&& request);
+
+    /**
      * For debugging!
      *
      * Sets up an empty connection. All queries to this connection which require a
@@ -131,7 +159,7 @@
     friend sp<RpcSession>;
     friend RpcServer;
     friend RpcState;
-    RpcSession();
+    explicit RpcSession(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
 
     /** This is not a pipe. */
     struct FdTrigger {
@@ -167,33 +195,35 @@
          *   true - succeeded in completely processing 'size'
          *   false - interrupted (failure or trigger)
          */
-        status_t interruptableReadFully(base::borrowed_fd fd, void* data, size_t size);
-        status_t interruptableWriteFully(base::borrowed_fd fd, const void* data, size_t size);
+        status_t interruptableReadFully(RpcTransport* rpcTransport, void* data, size_t size);
+        status_t interruptableWriteFully(RpcTransport* rpcTransport, const void* data, size_t size);
 
     private:
+        status_t triggerablePoll(RpcTransport* rpcTransport, int16_t event);
+
         base::unique_fd mWrite;
         base::unique_fd mRead;
     };
 
     class EventListener : public virtual RefBase {
     public:
-        virtual void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
+        virtual void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0;
         virtual void onSessionIncomingThreadEnded() = 0;
     };
 
     class WaitForShutdownListener : public EventListener {
     public:
-        void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
+        void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
         void onSessionIncomingThreadEnded() override;
         void waitForShutdown(std::unique_lock<std::mutex>& lock);
 
     private:
         std::condition_variable mCv;
-        bool mShutdown = false;
+        volatile bool mShutdown = false;
     };
 
     struct RpcConnection : public RefBase {
-        base::unique_fd fd;
+        std::unique_ptr<RpcTransport> rpcTransport;
 
         // whether this or another thread is currently using this fd to make
         // or receive transactions.
@@ -219,19 +249,24 @@
         // Status of setup
         status_t status;
     };
-    PreJoinSetupResult preJoinSetup(base::unique_fd fd);
+    PreJoinSetupResult preJoinSetup(std::unique_ptr<RpcTransport> rpcTransport);
     // join on thread passed to preJoinThreadOwnership
     static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
 
+    [[nodiscard]] bool setupClient(
+            const std::function<bool(const RpcAddress& sessionId, bool incoming)>& connectAndInit);
     [[nodiscard]] bool setupSocketClient(const RpcSocketAddress& address);
     [[nodiscard]] bool setupOneSocketConnection(const RpcSocketAddress& address,
-                                                const RpcAddress& sessionId, bool server);
-    [[nodiscard]] bool addIncomingConnection(base::unique_fd fd);
-    [[nodiscard]] bool addOutgoingConnection(base::unique_fd fd, bool init);
+                                                const RpcAddress& sessionId, bool incoming);
+    [[nodiscard]] bool initAndAddConnection(base::unique_fd fd, const RpcAddress& sessionId,
+                                            bool incoming);
+    [[nodiscard]] bool addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport);
+    [[nodiscard]] bool addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init);
     [[nodiscard]] bool setForServer(const wp<RpcServer>& server,
                                     const wp<RpcSession::EventListener>& eventListener,
                                     const RpcAddress& sessionId);
-    sp<RpcConnection> assignIncomingConnectionToThisThread(base::unique_fd fd);
+    sp<RpcConnection> assignIncomingConnectionToThisThread(
+            std::unique_ptr<RpcTransport> rpcTransport);
     [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection);
 
     enum class ConnectionUse {
@@ -264,6 +299,8 @@
         bool mReentrant = false;
     };
 
+    const std::unique_ptr<RpcTransportCtxFactory> mRpcTransportCtxFactory;
+
     // On the other side of a session, for each of mOutgoingConnections here, there should
     // be one of mIncomingConnections on the other side (and vice versa).
     //
@@ -291,12 +328,14 @@
     std::mutex mMutex; // for all below
 
     size_t mMaxThreads = 0;
+    std::optional<uint32_t> mProtocolVersion;
 
     std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
     size_t mWaitingThreads = 0;
     // hint index into clients, ++ when sending an async transaction
     size_t mOutgoingConnectionsOffset = 0;
     std::vector<sp<RpcConnection>> mOutgoingConnections;
+    size_t mMaxIncomingConnections = 0;
     std::vector<sp<RpcConnection>> mIncomingConnections;
     std::map<std::thread::id, std::thread> mThreads;
 };
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
new file mode 100644
index 0000000..1164600
--- /dev/null
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Wraps the transport layer of RPC. Implementation may use plain sockets or TLS.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+
+// Represents a socket connection.
+class RpcTransport {
+public:
+    virtual ~RpcTransport() = default;
+
+    // replacement of ::send(). errno may not be set if TLS is enabled.
+    virtual android::base::Result<size_t> send(const void *buf, size_t size) = 0;
+
+    // replacement of ::recv(). errno may not be set if TLS is enabled.
+    virtual android::base::Result<size_t> recv(void *buf, size_t size) = 0;
+
+    // replacement of ::recv(MSG_PEEK). errno may not be set if TLS is enabled.
+    //
+    // Implementation details:
+    // - For TLS, this may invoke syscalls and read data from the transport
+    // into an internal buffer in userspace. After that, pending() == true.
+    // - For raw sockets, this calls ::recv(MSG_PEEK), which leaves the data in the kernel buffer;
+    // pending() is always false.
+    virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+
+    // Returns true if there are data pending in a userspace buffer that RpcTransport holds.
+    //
+    // Implementation details:
+    // - For TLS, this does not invoke any syscalls or read any data from the
+    // transport. This only returns whether there are data pending in the internal buffer in
+    // userspace.
+    // - For raw sockets, this always returns false.
+    virtual bool pending() = 0;
+
+    // Returns fd for polling.
+    //
+    // Do not directly read / write on this raw fd!
+    [[nodiscard]] virtual android::base::borrowed_fd pollSocket() const = 0;
+
+protected:
+    RpcTransport() = default;
+};
+
+// Represents the context that generates the socket connection.
+class RpcTransportCtx {
+public:
+    virtual ~RpcTransportCtx() = default;
+    [[nodiscard]] virtual std::unique_ptr<RpcTransport> newTransport(
+            android::base::unique_fd fd) const = 0;
+
+protected:
+    RpcTransportCtx() = default;
+};
+
+// A factory class that generates RpcTransportCtx.
+class RpcTransportCtxFactory {
+public:
+    virtual ~RpcTransportCtxFactory() = default;
+    // Creates server context.
+    [[nodiscard]] virtual std::unique_ptr<RpcTransportCtx> newServerCtx() const = 0;
+
+    // Creates client context.
+    [[nodiscard]] virtual std::unique_ptr<RpcTransportCtx> newClientCtx() const = 0;
+
+    // Return a short description of this transport (e.g. "raw"). For logging / debugging / testing
+    // only.
+    [[nodiscard]] virtual const char *toCString() const = 0;
+
+protected:
+    RpcTransportCtxFactory() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcTransportRaw.h b/libs/binder/include/binder/RpcTransportRaw.h
new file mode 100644
index 0000000..6fb1f92
--- /dev/null
+++ b/libs/binder/include/binder/RpcTransportRaw.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses plain sockets.
+// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory with TLS disabled.
+class RpcTransportCtxFactoryRaw : public RpcTransportCtxFactory {
+public:
+    static std::unique_ptr<RpcTransportCtxFactory> make();
+
+    std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+    std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+    const char* toCString() const override;
+
+private:
+    RpcTransportCtxFactoryRaw() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 629b565..ce4362f 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -44,10 +44,9 @@
 //   to old servers, and new servers know how to interpret the 8-byte result,
 //   they can still communicate.
 //
-// Every binder object has a stability level associated with it, and when
-// communicating with a binder, we make sure that the command we sent is one
-// that it knows how to process. The summary of stability of a binder is
-// represented by a Stability::Category object.
+// This class is specifically about (1). (2) is not currently tracked by
+// libbinder for regular binder calls, and everything on the system uses the
+// same copy of libbinder.
 
 class Stability final {
 public:
@@ -128,7 +127,10 @@
 
     static void tryMarkCompilationUnit(IBinder* binder);
 
-    enum Level : uint8_t {
+    // Currently, we use int16_t for Level so that it can fit in BBinder.
+    // However, on the wire, we have 4 bytes reserved for stability, so whenever
+    // we ingest a Level, we always accept an int32_t.
+    enum Level : int16_t {
         UNDECLARED = 0,
 
         VENDOR = 0b000011,
@@ -136,37 +138,6 @@
         VINTF = 0b111111,
     };
 
-    // This is the format of stability passed on the wire. It is only 2 bytes
-    // long, but 2 bytes in addition to this are reserved here. The difference
-    // in size is in order to free up space in BBinder, which is fixed by
-    // prebuilts inheriting from it.
-    struct Category {
-        static inline Category fromRepr(int16_t representation) {
-            return *reinterpret_cast<Category*>(&representation);
-        }
-        int16_t repr() const { return *reinterpret_cast<const int16_t*>(this); }
-        static inline Category currentFromLevel(Level level);
-
-        bool operator== (const Category& o) const {
-            return repr() == o.repr();
-        }
-        bool operator!= (const Category& o) const {
-            return !(*this == o);
-        }
-
-        std::string debugString();
-
-        // This is the version of the wire protocol associated with the host
-        // process of a particular binder. As the wire protocol changes, if
-        // sending a transaction to a binder with an old version, the Parcel
-        // class must write parcels according to the version documented here.
-        uint8_t version;
-
-        // bitmask of Stability::Level
-        Level level;
-    };
-    static_assert(sizeof(Category) == sizeof(int16_t));
-
     // returns the stability according to how this was built
     static Level getLocalLevel();
 
@@ -179,18 +150,18 @@
       REPR_ALLOW_DOWNGRADE = 2,
     };
     // applies stability to binder if stability level is known
-    __attribute__((warn_unused_result))
-    static status_t setRepr(IBinder* binder, int32_t representation, uint32_t flags);
+    __attribute__((warn_unused_result)) static status_t setRepr(IBinder* binder, int32_t setting,
+                                                                uint32_t flags);
 
     // get stability information as encoded on the wire
-    static Category getCategory(IBinder* binder);
+    static int16_t getRepr(IBinder* binder);
 
     // whether a transaction on binder is allowed, if the transaction
     // is done from a context with a specific stability level
-    static bool check(Category provided, Level required);
+    static bool check(int16_t provided, Level required);
 
-    static bool isDeclaredLevel(Level level);
-    static std::string levelString(Level level);
+    static bool isDeclaredLevel(int32_t level);
+    static std::string levelString(int32_t level);
 
     Stability();
 };
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 488009f..8ee396e 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -71,7 +71,7 @@
     srcs: ["libbinder_ndk_unit_test.cpp"],
     static_libs: [
         "IBinderNdkUnitTest-cpp",
-        "IBinderNdkUnitTest-ndk_platform",
+        "IBinderNdkUnitTest-ndk",
     ],
     test_suites: [
         "general-tests",
@@ -88,8 +88,8 @@
     ],
     static_libs: [
         "IBinderVendorDoubleLoadTest-cpp",
-        "IBinderVendorDoubleLoadTest-ndk_platform",
-        "libbinder_aidl_test_stub-ndk_platform",
+        "IBinderVendorDoubleLoadTest-ndk",
+        "libbinder_aidl_test_stub-ndk",
     ],
     // critical that libbinder/libbinder_ndk are shared for VTS
     shared_libs: [
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index a3f7620..a0e991c 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -493,7 +493,7 @@
     assert_eq!(parcel.read::<i32>().unwrap(), 15);
     let start = parcel.get_data_position();
 
-    assert_eq!(parcel.read::<bool>().unwrap(), true);
+    assert!(parcel.read::<bool>().unwrap());
 
     unsafe {
         assert!(parcel.set_data_position(start).is_ok());
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 607860f..ecc61f4 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -50,7 +50,7 @@
         "libbinder_ndk",
     ],
     static_libs: [
-        "IBinderRustNdkInteropTest-ndk_platform",
+        "IBinderRustNdkInteropTest-ndk",
         "libbinder_ndk_rust_interop",
     ],
     test_suites: ["general-tests"],
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 4702e45..415ede1 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -90,7 +90,7 @@
 pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
     let service_name = CStr::from_ptr(service_name).to_str().unwrap();
     let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default());
-    match binder::add_service(&service_name, service.as_binder()) {
+    match binder::add_service(service_name, service.as_binder()) {
         Ok(_) => StatusCode::OK as c_int,
         Err(e) => e as c_int,
     }
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 62df9b7..a6e3f7d 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -47,6 +47,7 @@
 
 namespace {
 
+const char* kLocalInetAddress = "127.0.0.1";
 using ServiceRetriever = decltype(&android::IServiceManager::checkService);
 
 int Usage(const char* program) {
@@ -86,7 +87,7 @@
     }
     rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     unsigned int port;
-    if (!rpcServer->setupInetServer(0, &port)) {
+    if (!rpcServer->setupInetServer(kLocalInetAddress, 0, &port)) {
         LOG(ERROR) << "setupInetServer failed";
         return EX_SOFTWARE;
     }
@@ -199,7 +200,7 @@
     rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     rpcServer->setRootObject(service);
     unsigned int port;
-    if (!rpcServer->setupInetServer(0, &port)) {
+    if (!rpcServer->setupInetServer(kLocalInetAddress, 0, &port)) {
         LOG(ERROR) << "Unable to set up inet server";
         return EX_SOFTWARE;
     }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 24afcf6..b7ad199 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -160,7 +160,7 @@
     ],
     static_libs: [
         "binderRpcTestIface-cpp",
-        "binderRpcTestIface-ndk_platform",
+        "binderRpcTestIface-ndk",
     ],
     test_suites: ["general-tests"],
     require_root: true,
@@ -296,7 +296,7 @@
     ],
     static_libs: [
         "binderStabilityTestIface-cpp",
-        "binderStabilityTestIface-ndk_platform",
+        "binderStabilityTestIface-ndk",
     ],
 
     test_suites: ["device-tests", "vts"],
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
index 2d30c8d..307151c 100644
--- a/libs/binder/tests/binderClearBufTest.cpp
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/hex.h>
 #include <android-base/logging.h>
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
@@ -30,22 +31,6 @@
 
 const String16 kServerName = String16("binderClearBuf");
 
-std::string hexString(const void* bytes, size_t len) {
-    if (bytes == nullptr) return "<null>";
-
-    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
-    char chars[] = "0123456789abcdef";
-    std::string result;
-    result.resize(len * 2);
-
-    for (size_t i = 0; i < len; i++) {
-        result[2 * i] = chars[bytes8[i] >> 4];
-        result[2 * i + 1] = chars[bytes8[i] & 0xf];
-    }
-
-    return result;
-}
-
 class FooBar : public BBinder {
  public:
     enum {
@@ -83,7 +68,7 @@
             lastReply = reply.data();
             lastReplySize = reply.dataSize();
         }
-        *outBuffer = hexString(lastReply, lastReplySize);
+        *outBuffer = android::base::HexString(lastReply, lastReplySize);
         return result;
     }
 };
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 4c3225f..65db7f6 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1192,7 +1192,7 @@
         if (rpcServer == nullptr) return {};
         rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
         unsigned int port;
-        if (!rpcServer->setupInetServer(0, &port)) {
+        if (!rpcServer->setupInetServer("127.0.0.1", 0, &port)) {
             ADD_FAILURE() << "setupInetServer failed";
             return {};
         }
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index a457e67..5f4a7b5 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -18,21 +18,31 @@
 #include <android-base/logging.h>
 #include <benchmark/benchmark.h>
 #include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
 
 #include <thread>
 
+#include <signal.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 using android::BBinder;
+using android::defaultServiceManager;
 using android::IBinder;
 using android::interface_cast;
+using android::IPCThreadState;
+using android::IServiceManager;
 using android::OK;
+using android::ProcessState;
 using android::RpcServer;
 using android::RpcSession;
 using android::sp;
+using android::String16;
 using android::binder::Status;
 
 class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
@@ -46,28 +56,51 @@
     }
 };
 
-static sp<RpcSession> gSession = RpcSession::make();
+enum Transport {
+    KERNEL,
+    RPC,
+};
 
-void BM_getRootObject(benchmark::State& state) {
-    while (state.KeepRunning()) {
-        CHECK(gSession->getRootObject() != nullptr);
+static void EachTransport(benchmark::internal::Benchmark* b) {
+#ifdef __BIONIC__
+    b->Args({Transport::KERNEL});
+#endif
+    b->Args({Transport::RPC});
+}
+
+static sp<RpcSession> gSession = RpcSession::make();
+#ifdef __BIONIC__
+static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
+static sp<IBinder> gKernelBinder;
+#endif
+
+static sp<IBinder> getBinderForOptions(benchmark::State& state) {
+    Transport transport = static_cast<Transport>(state.range(0));
+    switch (transport) {
+#ifdef __BIONIC__
+        case KERNEL:
+            return gKernelBinder;
+#endif
+        case RPC:
+            return gSession->getRootObject();
+        default:
+            LOG(FATAL) << "Unknown transport value: " << transport;
+            return nullptr;
     }
 }
-BENCHMARK(BM_getRootObject);
 
 void BM_pingTransaction(benchmark::State& state) {
-    sp<IBinder> binder = gSession->getRootObject();
-    CHECK(binder != nullptr);
+    sp<IBinder> binder = getBinderForOptions(state);
 
     while (state.KeepRunning()) {
         CHECK_EQ(OK, binder->pingBinder());
     }
 }
-BENCHMARK(BM_pingTransaction);
+BENCHMARK(BM_pingTransaction)->Apply(EachTransport);
 
 void BM_repeatString(benchmark::State& state) {
-    sp<IBinder> binder = gSession->getRootObject();
-    CHECK(binder != nullptr);
+    sp<IBinder> binder = getBinderForOptions(state);
+
     sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
     CHECK(iface != nullptr);
 
@@ -92,7 +125,7 @@
         CHECK(ret.isOk()) << ret;
     }
 }
-BENCHMARK(BM_repeatString);
+BENCHMARK(BM_repeatString)->Apply(EachTransport);
 
 void BM_repeatBinder(benchmark::State& state) {
     sp<IBinder> binder = gSession->getRootObject();
@@ -109,7 +142,7 @@
         CHECK(ret.isOk()) << ret;
     }
 }
-BENCHMARK(BM_repeatBinder);
+BENCHMARK(BM_repeatBinder)->Apply(EachTransport);
 
 int main(int argc, char** argv) {
     ::benchmark::Initialize(&argc, argv);
@@ -118,13 +151,36 @@
     std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
     (void)unlink(addr.c_str());
 
-    std::thread([addr]() {
+    std::cerr << "Tests suffixes:" << std::endl;
+    std::cerr << "\t\\" << Transport::KERNEL << " is KERNEL" << std::endl;
+    std::cerr << "\t\\" << Transport::RPC << " is RPC" << std::endl;
+
+    if (0 == fork()) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
         sp<RpcServer> server = RpcServer::make();
         server->setRootObject(sp<MyBinderRpcBenchmark>::make());
         server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
         CHECK(server->setupUnixDomainServer(addr.c_str()));
         server->join();
-    }).detach();
+        exit(1);
+    }
+
+#ifdef __BIONIC__
+    if (0 == fork()) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+        CHECK_EQ(OK,
+                 defaultServiceManager()->addService(kKernelBinderInstance,
+                                                     sp<MyBinderRpcBenchmark>::make()));
+        IPCThreadState::self()->joinThreadPool();
+        exit(1);
+    }
+
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+
+    gKernelBinder = defaultServiceManager()->waitForService(kKernelBinderInstance);
+    CHECK_NE(nullptr, gKernelBinder.get());
+#endif
 
     for (size_t tries = 0; tries < 5; tries++) {
         usleep(10000);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 40ebd9c..36a6804 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -29,6 +29,8 @@
 #include <binder/ProcessState.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
+#include <binder/RpcTransportRaw.h>
 #include <gtest/gtest.h>
 
 #include <chrono>
@@ -40,6 +42,7 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include "../RpcSocketAddress.h" // for testing preconnected clients
 #include "../RpcState.h"   // for debugging
 #include "../vm_sockets.h" // for VMADDR_*
 
@@ -47,6 +50,25 @@
 
 namespace android {
 
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+              RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+const char* kLocalInetAddress = "127.0.0.1";
+
+enum class RpcSecurity { RAW };
+
+static inline std::vector<RpcSecurity> RpcSecurityValues() {
+    return {RpcSecurity::RAW};
+}
+
+static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity) {
+    switch (rpcSecurity) {
+        case RpcSecurity::RAW:
+            return RpcTransportCtxFactoryRaw::make();
+        default:
+            LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
+    }
+}
+
 TEST(BinderRpcParcel, EntireParcelFormatted) {
     Parcel p;
     p.writeInt32(3);
@@ -54,10 +76,17 @@
     EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
 }
 
-TEST(BinderRpc, SetExternalServer) {
+class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> {
+public:
+    static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
+        return newFactory(info.param)->toCString();
+    }
+};
+
+TEST_P(BinderRpcSimple, SetExternalServerTest) {
     base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     int sinkFd = sink.get();
-    auto server = RpcServer::make();
+    auto server = RpcServer::make(newFactory(GetParam()));
     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     ASSERT_FALSE(server->hasServer());
     ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
@@ -67,6 +96,19 @@
     ASSERT_EQ(sinkFd, retrieved.get());
 }
 
+TEST(BinderRpc, CannotUseNextWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \
@@ -368,12 +410,15 @@
 };
 
 enum class SocketType {
+    PRECONNECTED,
     UNIX,
     VSOCK,
     INET,
 };
-static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
-    switch (info.param) {
+static inline std::string PrintToString(SocketType socketType) {
+    switch (socketType) {
+        case SocketType::PRECONNECTED:
+            return "preconnected_uds";
         case SocketType::UNIX:
             return "unix_domain_socket";
         case SocketType::VSOCK:
@@ -386,7 +431,21 @@
     }
 }
 
-class BinderRpc : public ::testing::TestWithParam<SocketType> {
+static base::unique_fd connectToUds(const char* addrStr) {
+    UnixSocketAddress addr(addrStr);
+    base::unique_fd serverFd(
+            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    int savedErrno = errno;
+    CHECK(serverFd.ok()) << "Could not create socket " << addrStr << ": " << strerror(savedErrno);
+
+    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        int savedErrno = errno;
+        LOG(FATAL) << "Could not connect to socket " << addrStr << ": " << strerror(savedErrno);
+    }
+    return serverFd;
+}
+
+class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> {
 public:
     struct Options {
         size_t numThreads = 1;
@@ -394,13 +453,19 @@
         size_t numIncomingConnections = 0;
     };
 
+    static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+        auto [type, security] = info.param;
+        return PrintToString(type) + "_" + newFactory(security)->toCString();
+    }
+
     // This creates a new process serving an interface on a certain number of
     // threads.
     ProcessSession createRpcTestSocketServerProcess(
             const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) {
         CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
 
-        SocketType socketType = GetParam();
+        SocketType socketType = std::get<0>(GetParam());
+        RpcSecurity rpcSecurity = std::get<1>(GetParam());
 
         unsigned int vsockPort = allocateVsockPort();
         std::string addr = allocateSocketAddress();
@@ -408,7 +473,7 @@
 
         auto ret = ProcessSession{
                 .host = Process([&](android::base::borrowed_fd writeEnd) {
-                    sp<RpcServer> server = RpcServer::make();
+                    sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity));
 
                     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
                     server->setMaxThreads(options.numThreads);
@@ -416,6 +481,8 @@
                     unsigned int outPort = 0;
 
                     switch (socketType) {
+                        case SocketType::PRECONNECTED:
+                            [[fallthrough]];
                         case SocketType::UNIX:
                             CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
                             break;
@@ -423,7 +490,7 @@
                             CHECK(server->setupVsockServer(vsockPort));
                             break;
                         case SocketType::INET: {
-                            CHECK(server->setupInetServer(0, &outPort));
+                            CHECK(server->setupInetServer(kLocalInetAddress, 0, &outPort));
                             CHECK_NE(0, outPort);
                             break;
                         }
@@ -450,10 +517,16 @@
         }
 
         for (size_t i = 0; i < options.numSessions; i++) {
-            sp<RpcSession> session = RpcSession::make();
+            sp<RpcSession> session = RpcSession::make(newFactory(rpcSecurity));
             session->setMaxThreads(options.numIncomingConnections);
 
             switch (socketType) {
+                case SocketType::PRECONNECTED:
+                    if (session->setupPreconnectedClient({}, [=]() {
+                            return connectToUds(addr.c_str());
+                        }))
+                        goto success;
+                    break;
                 case SocketType::UNIX:
                     if (session->setupUnixDomainClient(addr.c_str())) goto success;
                     break;
@@ -1114,13 +1187,14 @@
 }
 
 static bool testSupportVsockLoopback() {
+    // We don't need to enable TLS to know if vsock is supported.
     unsigned int vsockPort = allocateVsockPort();
-    sp<RpcServer> server = RpcServer::make();
+    sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make());
     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     CHECK(server->setupVsockServer(vsockPort));
     server->start();
 
-    sp<RpcSession> session = RpcSession::make();
+    sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
     bool okay = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
     while (!server->shutdown()) usleep(10000);
     ALOGE("Detected vsock loopback supported: %d", okay);
@@ -1128,7 +1202,7 @@
 }
 
 static std::vector<SocketType> testSocketTypes() {
-    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+    std::vector<SocketType> ret = {SocketType::PRECONNECTED, SocketType::UNIX, SocketType::INET};
 
     static bool hasVsockLoopback = testSupportVsockLoopback();
 
@@ -1139,10 +1213,13 @@
     return ret;
 }
 
-INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(testSocketTypes()),
-                        PrintSocketType);
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+                        ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
+                                           ::testing::ValuesIn(RpcSecurityValues())),
+                        BinderRpc::PrintParamInfo);
 
-class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+class BinderRpcServerRootObject
+      : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
 
 TEST_P(BinderRpcServerRootObject, WeakRootObject) {
     using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
@@ -1150,8 +1227,8 @@
         return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
     };
 
-    auto server = RpcServer::make();
-    auto [isStrong1, isStrong2] = GetParam();
+    auto [isStrong1, isStrong2, rpcSecurity] = GetParam();
+    auto server = RpcServer::make(newFactory(rpcSecurity));
     auto binder1 = sp<BBinder>::make();
     IBinder* binderRaw1 = binder1.get();
     setRootObject(isStrong1)(server.get(), binder1);
@@ -1168,7 +1245,8 @@
 }
 
 INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
-                        ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+                        ::testing::Combine(::testing::Bool(), ::testing::Bool(),
+                                           ::testing::ValuesIn(RpcSecurityValues())));
 
 class OneOffSignal {
 public:
@@ -1191,10 +1269,10 @@
     bool mValue = false;
 };
 
-TEST(BinderRpc, Shutdown) {
+TEST_P(BinderRpcSimple, Shutdown) {
     auto addr = allocateSocketAddress();
     unlink(addr.c_str());
-    auto server = RpcServer::make();
+    auto server = RpcServer::make(newFactory(GetParam()));
     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     ASSERT_TRUE(server->setupUnixDomainServer(addr.c_str()));
     auto joinEnds = std::make_shared<OneOffSignal>();
@@ -1237,7 +1315,7 @@
     auto rpcServer = RpcServer::make();
     rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
     unsigned int port;
-    ASSERT_TRUE(rpcServer->setupInetServer(0, &port));
+    ASSERT_TRUE(rpcServer->setupInetServer(kLocalInetAddress, 0, &port));
     auto socket = rpcServer->releaseServer();
 
     auto keepAlive = sp<BBinder>::make();
@@ -1255,6 +1333,9 @@
     ASSERT_EQ(OK, rpcBinder->pingBinder());
 }
 
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
+                        BinderRpcSimple::PrintTestParam);
+
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 74b8eb8..acf3f8f 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -23,7 +23,6 @@
         "main.cpp",
         "random_fd.cpp",
         "random_parcel.cpp",
-        "util.cpp",
     ],
     static_libs: [
         "libbase",
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index a717ce9..f1fe164 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -18,11 +18,13 @@
 #include "binder.h"
 #include "util.h"
 
+#include <android-base/hex.h>
 #include <android/os/IServiceManager.h>
 #include <binder/ParcelableHolder.h>
 #include <binder/PersistableBundle.h>
 
 using ::android::status_t;
+using ::android::base::HexString;
 
 enum ByteEnum : int8_t {};
 enum IntEnum : int32_t {};
@@ -128,7 +130,7 @@
     [] (const ::android::Parcel& p, uint8_t len) {
         FUZZ_LOG() << "about to readInplace";
         const void* r = p.readInplace(len);
-        FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len);
+        FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << (r ? HexString(r, len) : "null");
     },
     PARCEL_READ_OPT_STATUS(int32_t, readInt32),
     PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
@@ -153,7 +155,7 @@
         FUZZ_LOG() << "about to readString8Inplace";
         size_t outLen = 0;
         const char* str = p.readString8Inplace(&outLen);
-        std::string bytes = hexString(str, sizeof(char) * (outLen + 1));
+        std::string bytes = str ? HexString(str, sizeof(char) * (outLen + 1)) : "null";
         FUZZ_LOG() << "readString8Inplace: " << bytes << " size: " << outLen;
     },
     PARCEL_READ_OPT_STATUS(android::String16, readString16),
@@ -163,7 +165,7 @@
         FUZZ_LOG() << "about to readString16Inplace";
         size_t outLen = 0;
         const char16_t* str = p.readString16Inplace(&outLen);
-        std::string bytes = hexString(str, sizeof(char16_t) * (outLen + 1));
+        std::string bytes = str ? HexString(str, sizeof(char16_t) * (outLen + 1)) : "null";
         FUZZ_LOG() << "readString16Inplace: " << bytes << " size: " << outLen;
     },
     PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
index 35b5ebc..ee9840f 100644
--- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
@@ -18,10 +18,12 @@
 #include "hwbinder.h"
 #include "util.h"
 
+#include <android-base/hex.h>
 #include <android-base/logging.h>
 #include <hwbinder/Parcel.h>
 
 using ::android::status_t;
+using ::android::base::HexString;
 
 // TODO: support scatter-gather types
 
@@ -70,13 +72,13 @@
         FUZZ_LOG() << "about to read";
         std::vector<uint8_t> data (length);
         status_t status = p.read(data.data(), length);
-        FUZZ_LOG() << "read status: " << status << " data: " << hexString(data.data(), data.size());
+        FUZZ_LOG() << "read status: " << status << " data: " << HexString(data.data(), data.size());
     },
     [] (const ::android::hardware::Parcel& p, uint8_t length) {
         FUZZ_LOG() << "about to read";
         std::vector<uint8_t> data (length);
         const void* inplace = p.readInplace(length);
-        FUZZ_LOG() << "read status: " << hexString(inplace, length);
+        FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null");
     },
     PARCEL_READ_WITH_STATUS(int8_t, readInt8),
     PARCEL_READ_WITH_STATUS(uint8_t, readUint8),
@@ -100,7 +102,7 @@
         FUZZ_LOG() << "about to readString16Inplace";
         size_t outSize = 0;
         const char16_t* str = p.readString16Inplace(&outSize);
-        FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outSize);
+        FUZZ_LOG() << "readString16Inplace: " << HexString(str, sizeof(char16_t) * outSize);
     },
     PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
     PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 2a79e85..f435dae 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -22,6 +22,7 @@
 
 #include <iostream>
 
+#include <android-base/hex.h>
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_libbinder.h>
@@ -35,6 +36,7 @@
 
 using android::fillRandomParcel;
 using android::sp;
+using android::base::HexString;
 
 void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
     // TODO: functionality to create random parcels for libhwbinder parcels
@@ -78,8 +80,8 @@
     CHECK(reads.size() <= 255) << reads.size();
 
     FUZZ_LOG() << "backend: " << backend;
-    FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
-    FUZZ_LOG() << "instructions: " << hexString(instructions);
+    FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
+    FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size());
 
     for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
         uint8_t a = instructions[i];
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 92fdc72..a2472f8 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <binder/IServiceManager.h>
 #include <binder/RpcSession.h>
+#include <binder/RpcTransportRaw.h>
 #include <fuzzbinder/random_fd.h>
 #include <utils/String16.h>
 
@@ -35,7 +36,7 @@
 
 void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
     if (provider.ConsumeBool()) {
-        auto session = sp<RpcSession>::make();
+        auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK(session->addNullDebuggingClient());
         p->markForRpc(session);
         fillRandomParcelData(p, std::move(provider));
diff --git a/libs/binder/tests/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
deleted file mode 100644
index 479f406..0000000
--- a/libs/binder/tests/parcel_fuzzer/util.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-#define FUZZ_LOG_TAG "util"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <iomanip>
-#include <sstream>
-
-std::string hexString(const void* bytes, size_t len) {
-    if (bytes == nullptr) return "<null>";
-
-    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
-    char chars[] = "0123456789abcdef";
-    std::string result;
-    result.resize(len * 2);
-
-    for (size_t i = 0; i < len; i++) {
-        result[2 * i] = chars[bytes8[i] >> 4];
-        result[2 * i + 1] = chars[bytes8[i] & 0xf];
-    }
-
-    return result;
-}
-std::string hexString(const std::vector<uint8_t>& bytes) {
-    return hexString(bytes.data(), bytes.size());
-}
diff --git a/libs/binder/tests/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
index 45e8c57..a5d0dae 100644
--- a/libs/binder/tests/parcel_fuzzer/util.h
+++ b/libs/binder/tests/parcel_fuzzer/util.h
@@ -49,6 +49,3 @@
     FuzzLog& log() { return *this; }
 };
 #endif
-
-std::string hexString(const void* bytes, size_t len);
-std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index a0032ae..cda9e19 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -52,14 +52,14 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
     ],
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b0dc27f..326da3a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -67,6 +67,8 @@
         ":guiconstants_aidl",
         "android/gui/FocusRequest.aidl",
         "android/gui/InputApplicationInfo.aidl",
+        "android/gui/IWindowInfosListener.aidl",
+        "android/gui/IWindowInfosReportedListener.aidl",
         "android/gui/WindowInfo.aidl",
         "WindowInfo.cpp",
     ],
@@ -195,6 +197,7 @@
         "SyncFeatures.cpp",
         "TransactionTracing.cpp",
         "view/Surface.cpp",
+        "WindowInfosListenerReporter.cpp",
         "bufferqueue/1.0/B2HProducerListener.cpp",
         "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
         "bufferqueue/2.0/B2HProducerListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index d9024fa..70f2ae7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -166,9 +166,10 @@
     mTransformHint = mSurfaceControl->getTransformHint();
     mBufferItemConsumer->setTransformHint(mTransformHint);
     SurfaceComposerClient::Transaction()
-          .setFlags(surface, layer_state_t::eEnableBackpressure,
-                    layer_state_t::eEnableBackpressure)
-          .apply();
+            .setFlags(surface, layer_state_t::eEnableBackpressure,
+                      layer_state_t::eEnableBackpressure)
+            .setApplyToken(mApplyToken)
+            .apply();
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
     BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width,
@@ -190,7 +191,7 @@
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
-                              int32_t format) {
+                              int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
     std::unique_lock _lock{mMutex};
     if (mFormat != format) {
         mFormat = format;
@@ -227,15 +228,18 @@
             // We only need to update the scale if we've received at least one buffer. The reason
             // for this is the scale is calculated based on the requested size and buffer size.
             // If there's no buffer, the scale will always be 1.
+            SurfaceComposerClient::Transaction* destFrameTransaction =
+                    (outTransaction) ? outTransaction : &t;
             if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) {
-                t.setDestinationFrame(mSurfaceControl,
-                                      Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
+                destFrameTransaction->setDestinationFrame(mSurfaceControl,
+                                                          Rect(0, 0, newSize.getWidth(),
+                                                               newSize.getHeight()));
             }
             applyTransaction = true;
         }
     }
     if (applyTransaction) {
-        t.apply();
+        t.setApplyToken(mApplyToken).apply();
     }
 }
 
@@ -453,6 +457,9 @@
     incStrong((void*)transactionCallbackThunk);
 
     Rect crop = computeCrop(bufferItem);
+    const bool updateDestinationFrame =
+            bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE ||
+            !mLastBufferInfo.hasBuffer;
     mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
                            bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
                            bufferItem.mScalingMode, crop);
@@ -470,7 +477,9 @@
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
     mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
-    t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+    if (updateDestinationFrame) {
+        t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+    }
     t->setBufferCrop(mSurfaceControl, crop);
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5023b6b..2930154 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -34,7 +34,6 @@
 #include <gui/BufferQueueCore.h>
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
 #include <system/window.h>
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index bab9ebf..3bf6306 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -44,6 +44,7 @@
 
 namespace android {
 
+using gui::IWindowInfosListener;
 using ui::ColorMode;
 
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
@@ -1232,6 +1233,22 @@
 
         return reply.readInt32(buffers);
     }
+
+    status_t addWindowInfosListener(
+            const sp<IWindowInfosListener>& windowInfosListener) const override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
+        return remote()->transact(BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, data, &reply);
+    }
+
+    status_t removeWindowInfosListener(
+            const sp<IWindowInfosListener>& windowInfosListener) const override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
+        return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -2115,6 +2132,20 @@
             SAFE_PARCEL(reply->writeBool, success);
             return err;
         }
+        case ADD_WINDOW_INFOS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IWindowInfosListener> listener;
+            SAFE_PARCEL(data.readStrongBinder, &listener);
+
+            return addWindowInfosListener(listener);
+        }
+        case REMOVE_WINDOW_INFOS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IWindowInfosListener> listener;
+            SAFE_PARCEL(data.readStrongBinder, &listener);
+
+            return removeWindowInfosListener(listener);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d73470d..23895c0 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -67,6 +67,7 @@
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         autoRefresh(false),
+        isTrustedOverlay(false),
         bufferCrop(Rect::INVALID_RECT),
         destinationFrame(Rect::INVALID_RECT),
         releaseBufferListener(nullptr) {
@@ -171,6 +172,7 @@
     SAFE_PARCEL(output.write, stretchEffect);
     SAFE_PARCEL(output.write, bufferCrop);
     SAFE_PARCEL(output.write, destinationFrame);
+    SAFE_PARCEL(output.writeBool, isTrustedOverlay);
 
     return NO_ERROR;
 }
@@ -299,6 +301,7 @@
     SAFE_PARCEL(input.read, stretchEffect);
     SAFE_PARCEL(input.read, bufferCrop);
     SAFE_PARCEL(input.read, destinationFrame);
+    SAFE_PARCEL(input.readBool, &isTrustedOverlay);
 
     return NO_ERROR;
 }
@@ -534,6 +537,10 @@
         what |= eAutoRefreshChanged;
         autoRefresh = other.autoRefresh;
     }
+    if (other.what & eTrustedOverlayChanged) {
+        what |= eTrustedOverlayChanged;
+        isTrustedOverlay = other.isTrustedOverlay;
+    }
     if (other.what & eReleaseBufferListenerChanged) {
         if (releaseBufferListener) {
             ALOGW("Overriding releaseBufferListener");
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index ec03c21..f620f5a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/gui/IWindowInfosListener.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/SortedVector.h>
@@ -54,6 +55,7 @@
 using gui::FocusRequest;
 using gui::WindowInfo;
 using gui::WindowInfoHandle;
+using gui::WindowInfosListener;
 using ui::ColorMode;
 // ---------------------------------------------------------------------------
 
@@ -95,6 +97,7 @@
     if (instance.mComposerService == nullptr) {
         if (ComposerService::getInstance().connectLocked()) {
             ALOGD("ComposerService reconnected");
+            WindowInfosListenerReporter::getInstance()->reconnect(instance.mComposerService);
         }
     }
     return instance.mComposerService;
@@ -1652,6 +1655,19 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrustedOverlay(
+        const sp<SurfaceControl>& sc, bool isTrustedOverlay) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eTrustedOverlayChanged;
+    s->isTrustedOverlay = isTrustedOverlay;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
         const sp<IBinder>& applyToken) {
     mApplyToken = applyToken;
@@ -1768,15 +1784,10 @@
 
 // ---------------------------------------------------------------------------
 
-SurfaceComposerClient::SurfaceComposerClient()
-    : mStatus(NO_INIT)
-{
-}
+SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {}
 
 SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client)
-    : mStatus(NO_ERROR), mClient(client)
-{
-}
+      : mStatus(NO_ERROR), mClient(client) {}
 
 void SurfaceComposerClient::onFirstRef() {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -2142,6 +2153,18 @@
     return ComposerService::getComposerService()->getGPUContextPriority();
 }
 
+status_t SurfaceComposerClient::addWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener) {
+    return WindowInfosListenerReporter::getInstance()
+            ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService());
+}
+
+status_t SurfaceComposerClient::removeWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener) {
+    return WindowInfosListenerReporter::getInstance()
+            ->removeWindowInfosListener(windowInfosListener, ComposerService::getComposerService());
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
new file mode 100644
index 0000000..c00a438
--- /dev/null
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosListenerReporter.h>
+
+namespace android {
+
+using gui::IWindowInfosReportedListener;
+using gui::WindowInfo;
+using gui::WindowInfosListener;
+
+sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() {
+    static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter;
+    return sInstance;
+}
+
+status_t WindowInfosListenerReporter::addWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener,
+        const sp<ISurfaceComposer>& surfaceComposer) {
+    status_t status = OK;
+    {
+        std::scoped_lock lock(mListenersMutex);
+        if (mWindowInfosListeners.empty()) {
+            status = surfaceComposer->addWindowInfosListener(this);
+        }
+
+        if (status == OK) {
+            mWindowInfosListeners.insert(windowInfosListener);
+        }
+    }
+
+    return status;
+}
+
+status_t WindowInfosListenerReporter::removeWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener,
+        const sp<ISurfaceComposer>& surfaceComposer) {
+    status_t status = OK;
+    {
+        std::scoped_lock lock(mListenersMutex);
+        if (mWindowInfosListeners.size() == 1) {
+            status = surfaceComposer->removeWindowInfosListener(this);
+        }
+
+        if (status == OK) {
+            mWindowInfosListeners.erase(windowInfosListener);
+        }
+    }
+
+    return status;
+}
+
+binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
+        const std::vector<WindowInfo>& windowInfos,
+        const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
+    std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
+            windowInfosListeners;
+
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (auto listener : mWindowInfosListeners) {
+            windowInfosListeners.insert(listener);
+        }
+    }
+
+    for (auto listener : windowInfosListeners) {
+        listener->onWindowInfosChanged(windowInfos);
+    }
+
+    if (windowInfosReportedListener) {
+        windowInfosReportedListener->onWindowInfosReported();
+    }
+
+    return binder::Status::ok();
+}
+
+void WindowInfosListenerReporter::reconnect(const sp<ISurfaceComposer>& composerService) {
+    std::scoped_lock lock(mListenersMutex);
+    if (!mWindowInfosListeners.empty()) {
+        composerService->addWindowInfosListener(this);
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
new file mode 100644
index 0000000..d4553ca
--- /dev/null
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.IWindowInfosReportedListener;
+import android.gui.WindowInfo;
+
+/** @hide */
+oneway interface IWindowInfosListener
+{
+    void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+}
diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/gui/android/gui/IWindowInfosReportedListener.aidl
similarity index 85%
rename from libs/input/android/os/ISetInputWindowsListener.aidl
rename to libs/gui/android/gui/IWindowInfosReportedListener.aidl
index bb58fb6..0e4cce6 100644
--- a/libs/input/android/os/ISetInputWindowsListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosReportedListener.aidl
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.gui;
 
 /** @hide */
-oneway interface ISetInputWindowsListener
+oneway interface IWindowInfosReportedListener
 {
-    void onSetInputWindowsFinished();
+    void onWindowInfosReported();
 }
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 0d6a673..ea9b1c6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -96,7 +96,8 @@
     void setTransactionCompleteCallback(uint64_t frameNumber,
                                         std::function<void(int64_t)>&& transactionCompleteCallback);
 
-    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
+    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
+                SurfaceComposerClient::Transaction* outTransaction = nullptr);
     void flushShadowQueue() {}
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index c0a2335..b7cd082 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,6 +22,7 @@
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
+#include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 #include <ftl/Flags.h>
@@ -550,6 +551,11 @@
      * in MIN_UNDEQUEUED_BUFFERS.
      */
     virtual status_t getMaxAcquiredBufferCount(int* buffers) const = 0;
+
+    virtual status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0;
+    virtual status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -622,6 +628,8 @@
         ON_PULL_ATOM,
         ADD_TUNNEL_MODE_ENABLED_LISTENER,
         REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
+        ADD_WINDOW_INFOS_LISTENER,
+        REMOVE_WINDOW_INFOS_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index d1a9a04..b27d9cf 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -116,6 +116,7 @@
         eBlurRegionsChanged = 0x800'00000000,
         eAutoRefreshChanged = 0x1000'00000000,
         eStretchChanged = 0x2000'00000000,
+        eTrustedOverlayChanged = 0x4000'00000000,
     };
 
     layer_state_t();
@@ -221,6 +222,10 @@
     // in shared buffer mode.
     bool autoRefresh;
 
+    // An inherited state that indicates that this surface control and its children
+    // should be trusted for input occlusion detection purposes
+    bool isTrustedOverlay;
+
     // Stretch effect to be applied to this layer
     StretchEffect stretchEffect;
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 1dfc83b..869cef6 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,6 +42,7 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
+#include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
 
 namespace android {
@@ -535,6 +536,9 @@
         // in shared buffer mode.
         Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
 
+        // Sets that this surface control and its children are trusted overlays for input
+        Transaction& setTrustedOverlay(const sp<SurfaceControl>& sc, bool isTrustedOverlay);
+
         // Queues up transactions using this token in SurfaceFlinger.  By default, all transactions
         // from a client are placed on the same queue. This can be used to prevent multiple
         // transactions from blocking each other.
@@ -619,6 +623,9 @@
     static status_t removeTunnelModeEnabledListener(
             const sp<gui::ITunnelModeEnabledListener>& listener);
 
+    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+    status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+
 private:
     virtual void onFirstRef();
 
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b0c631a..7af1f0e 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -242,18 +242,6 @@
     }
 
     /**
-     * Requests that the state of this object be updated to reflect
-     * the most current available information about the application.
-     * As this class is created as RefBase object, no pure virtual function is allowed.
-     *
-     * This method should only be called from within the input dispatcher's
-     * critical section.
-     *
-     * Returns true on success, or false if the handle is no longer valid.
-     */
-    virtual bool updateInfo() { return false; }
-
-    /**
      * Updates from another input window handle.
      */
     void updateFrom(const sp<WindowInfoHandle> handle);
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
new file mode 100644
index 0000000..8a70b9b
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/WindowInfo.h>
+#include <utils/RefBase.h>
+
+namespace android::gui {
+
+class WindowInfosListener : public virtual RefBase {
+public:
+    virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+};
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
new file mode 100644
index 0000000..7cb96e0
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/BnWindowInfosListener.h>
+#include <android/gui/IWindowInfosReportedListener.h>
+#include <binder/IBinder.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosListener.h>
+#include <utils/Mutex.h>
+#include <unordered_set>
+
+namespace android {
+class ISurfaceComposer;
+
+class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
+public:
+    static sp<WindowInfosListenerReporter> getInstance();
+    binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+                                        const sp<gui::IWindowInfosReportedListener>&) override;
+
+    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
+                                    const sp<ISurfaceComposer>&);
+    status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
+                                       const sp<ISurfaceComposer>&);
+    void reconnect(const sp<ISurfaceComposer>&);
+
+private:
+    std::mutex mListenersMutex;
+    std::unordered_set<sp<gui::WindowInfosListener>,
+                       ISurfaceComposer::SpHash<gui::WindowInfosListener>>
+            mWindowInfosListeners GUARDED_BY(mListenersMutex);
+};
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8d7a356..d1ad478 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -902,6 +902,16 @@
 
     status_t getMaxAcquiredBufferCount(int* /*buffers*/) const override { return NO_ERROR; }
 
+    status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override {
+        return NO_ERROR;
+    }
+
+    status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override {
+        return NO_ERROR;
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 1a142c9..e73c3b8 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -68,12 +68,10 @@
 
     static_libs: [
         "libui-types",
-        "libgui_window_info_static",
     ],
 
     export_static_lib_headers: [
         "libui-types",
-        "libgui_window_info_static",
     ],
 
     target: {
@@ -85,7 +83,6 @@
                 "android/os/IInputFlinger.aidl",
                 "android/os/InputEventInjectionResult.aidl",
                 "android/os/InputEventInjectionSync.aidl",
-                "android/os/ISetInputWindowsListener.aidl",
             ],
 
             export_shared_lib_headers: ["libbinder"],
@@ -96,6 +93,14 @@
                 "libui",
             ],
 
+            static_libs: [
+                "libgui_window_info_static",
+            ],
+
+            export_static_lib_headers: [
+                "libgui_window_info_static",
+            ],
+
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -113,14 +118,18 @@
                 "InputTransport.cpp",
                 "android/os/IInputConstants.aidl",
                 "android/os/IInputFlinger.aidl",
-                "android/os/ISetInputWindowsListener.aidl",
             ],
             static_libs: [
                 "libhostgraphics",
+                "libgui_window_info_static",
             ],
             shared_libs: [
                 "libbinder",
             ],
+
+            export_static_lib_headers: [
+                "libgui_window_info_static",
+            ],
         },
     },
 
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 82a814f..1e8ff94 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -23,6 +23,7 @@
 #include <limits.h>
 #include <string.h>
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <gui/constants.h>
 #include <input/Input.h>
@@ -42,6 +43,15 @@
 
 namespace {
 
+// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
+// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
+bool isPerWindowInputRotationEnabled() {
+    static const bool PER_WINDOW_INPUT_ROTATION =
+            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
+
+    return PER_WINDOW_INPUT_ROTATION;
+}
+
 float transformAngle(const ui::Transform& transform, float angleRadians) {
     // Construct and transform a vector oriented at the specified clockwise angle from vertical.
     // Coordinate system: down is increasing Y, right is increasing X.
@@ -160,6 +170,9 @@
         case AINPUT_EVENT_TYPE_DRAG: {
             return "DRAG";
         }
+        case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+            return "TOUCH_MODE";
+        }
     }
     return "UNKNOWN";
 }
@@ -526,6 +539,8 @@
                                              size_t historicalIndex) const {
     const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
 
+    if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
+
     if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
         // For compatibility, convert raw coordinates into "oriented screen space". Once app
         // developers are educated about getRaw, we can consider removing this.
@@ -883,6 +898,19 @@
     mY = from.mY;
 }
 
+// --- TouchModeEvent ---
+
+void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
+    InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
+    mIsInTouchMode = isInTouchMode;
+}
+
+void TouchModeEvent::initialize(const TouchModeEvent& from) {
+    InputEvent::initialize(from);
+    mIsInTouchMode = from.mIsInTouchMode;
+}
+
 // --- PooledInputEventFactory ---
 
 PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -937,6 +965,15 @@
     return event;
 }
 
+TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() {
+    if (mTouchModeEventPool.empty()) {
+        return new TouchModeEvent();
+    }
+    TouchModeEvent* event = mTouchModeEventPool.front().release();
+    mTouchModeEventPool.pop();
+    return event;
+}
+
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ea8b9a7..1e93dfb 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -116,6 +116,7 @@
         case Type::FOCUS:
         case Type::CAPTURE:
         case Type::DRAG:
+        case Type::TOUCH_MODE:
             return true;
         case Type::TIMELINE: {
             const nsecs_t gpuCompletedTime =
@@ -151,6 +152,8 @@
             return sizeof(Header) + body.drag.size();
         case Type::TIMELINE:
             return sizeof(Header) + body.timeline.size();
+        case Type::TOUCH_MODE:
+            return sizeof(Header) + body.touchMode.size();
     }
     return sizeof(Header);
 }
@@ -293,6 +296,10 @@
             msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
             break;
         }
+        case InputMessage::Type::TOUCH_MODE: {
+            msg->body.touchMode.eventId = body.touchMode.eventId;
+            msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode;
+        }
     }
 }
 
@@ -665,6 +672,22 @@
     return mChannel->sendMessage(&msg);
 }
 
+status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
+                             mChannel->getName().c_str(), toString(isInTouchMode));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::TOUCH_MODE;
+    msg.header.seq = seq;
+    msg.body.touchMode.eventId = eventId;
+    msg.body.touchMode.isInTouchMode = isInTouchMode;
+    return mChannel->sendMessage(&msg);
+}
+
 android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
@@ -866,6 +889,16 @@
                 *outEvent = dragEvent;
                 break;
             }
+
+            case InputMessage::Type::TOUCH_MODE: {
+                TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+                if (!touchModeEvent) return NO_MEMORY;
+
+                initializeTouchModeEvent(touchModeEvent, &mMsg);
+                *outSeq = mMsg.header.seq;
+                *outEvent = touchModeEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1370,6 +1403,10 @@
                       msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
 }
 
+void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
+}
+
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
     uint32_t pointerCount = msg->body.motion.pointerCount;
     PointerCoords pointerCoords[pointerCount];
@@ -1476,6 +1513,11 @@
                                                        presentTime);
                     break;
                 }
+                case InputMessage::Type::TOUCH_MODE: {
+                    out += android::base::StringPrintf("isInTouchMode=%s",
+                                                       toString(msg.body.touchMode.isInTouchMode));
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 43b262f..00ebd4d 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -19,17 +19,10 @@
 import android.InputChannel;
 import android.gui.FocusRequest;
 import android.gui.WindowInfo;
-import android.os.ISetInputWindowsListener;
 
 /** @hide */
 interface IInputFlinger
 {
-    // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
-    // ordering when needed.
-    // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
-    // shouldn't be a concern.
-    oneway void setInputWindows(in WindowInfo[] windowInfoHandles,
-            in @nullable ISetInputWindowsListener setInputWindowsListener);
     InputChannel createInputChannel(in @utf8InCpp String name);
     void removeInputChannel(in IBinder connectionToken);
     /**
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a2fa319..b1ef753 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -17,6 +17,7 @@
 #include <array>
 #include <math.h>
 
+#include <android-base/properties.h>
 #include <attestation/HmacKeyManager.h>
 #include <binder/Parcel.h>
 #include <gtest/gtest.h>
@@ -226,13 +227,34 @@
     static constexpr float X_OFFSET = 1;
     static constexpr float Y_OFFSET = 1.1;
 
+    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
+
     int32_t mId;
     ui::Transform mTransform;
 
+    void SetUp() override;
+    void TearDown() override;
+
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
+const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
+        !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
+        ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
+        : std::nullopt;
+
+void MotionEventTest::SetUp() {
+    // Ensure per_window_input_rotation is enabled.
+    base::SetProperty("persist.debug.per_window_input_rotation", "true");
+}
+
+void MotionEventTest::TearDown() {
+    const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
+            ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
+            : "";
+    base::SetProperty("persist.debug.per_window_input_rotation", val);
+}
 
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     mId = InputEvent::nextId();
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 5d1f2c3..8db5bf1 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -56,6 +56,7 @@
     void PublishAndConsumeFocusEvent();
     void PublishAndConsumeCaptureEvent();
     void PublishAndConsumeDragEvent();
+    void PublishAndConsumeTouchModeEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -413,6 +414,46 @@
             << "finished signal's consume time should be greater than publish time";
 }
 
+void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    int32_t eventId = InputEvent::nextId();
+    constexpr bool touchModeEnabled = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+    ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+            << "consumer should have returned a touch mode event";
+
+    const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(eventId, touchModeEvent.getId());
+    EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
+}
+
 TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
     const int32_t inputEventId = 20;
     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
@@ -449,6 +490,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -520,6 +565,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
 }
 
 } // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 59fed1f..18289a5 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -102,6 +102,10 @@
   CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
   CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
+
+  CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);
 }
 
 void TestHeaderSize() {
@@ -123,6 +127,7 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
     static_assert(sizeof(InputMessage::Body::Drag) == 16);
+    static_assert(sizeof(InputMessage::Body::TouchMode) == 8);
     // Timeline
     static_assert(GraphicsTimeline::SIZE == 2);
     static_assert(sizeof(InputMessage::Body::Timeline) == 24);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 8a677ce..e2f32e3 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -698,6 +698,10 @@
             "gralloc and AHardwareBuffer flags don't match");
     static_assert(AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE == (uint64_t)BufferUsage::GPU_MIPMAP_COMPLETE,
             "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CAMERA_WRITE == (uint64_t)BufferUsage::CAMERA_OUTPUT,
+            "gralloc and AHardwareBuffer flags don't match");
+    static_assert(AHARDWAREBUFFER_USAGE_CAMERA_READ == (uint64_t)BufferUsage::CAMERA_INPUT,
+            "gralloc and AHardwareBuffer flags don't match");
     return usage;
 }
 
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 50fe0b7..21931bb 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -91,6 +91,20 @@
     AHARDWAREBUFFER_FORMAT_YCbCr_422_I              = 0x14,
 };
 
+/**
+ * Buffer usage flags.
+ */
+enum {
+    /* for future proofing, keep these in sync with hardware/gralloc.h */
+
+    /* The buffer will be written by the HW camera pipeline. */
+    AHARDWAREBUFFER_USAGE_CAMERA_WRITE              = 2UL << 16,
+    /* The buffer will be read by the HW camera pipeline. */
+    AHARDWAREBUFFER_USAGE_CAMERA_READ               = 4UL << 16,
+    /* Mask for the camera access values. */
+    AHARDWAREBUFFER_USAGE_CAMERA_MASK               = 6UL << 16,
+};
+
 __END_DECLS
 
 #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index ae8f238..01df6a6 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -95,25 +95,28 @@
             .alpha = 1,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer, &caster};
-    // When sourceDataspace matches dest, the general shadow fragment shader doesn't
-    // have color correction added.
-    // independently, when it is not srgb, the *vertex* shader has color correction added.
-    // This may be a bug, but the shader still needs to be cached as it is triggered
-    // during youtube pip.
-    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
-        layer.sourceDataspace = dataspace;
-        // The 2nd matrix, which has different scales for x and y, will
-        // generate the slower (more general case) shadow shader
-        for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
-            layer.geometry.positionTransform = transform;
-            caster.geometry.positionTransform = transform;
-            for (bool translucent : {false, true}){
-                layer.shadow.casterIsTranslucent = translucent;
-                renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                        base::unique_fd(), nullptr);
-            }
-        }
+    // Four combinations of settings are used (two transforms here, and drawShadowLayers is
+    // called with two different destination data spaces) They're all rounded rect.
+    // Three of these are cache misses that generate new shaders.
+    // The first combination generates a short and simple shadow shader.
+    // The second combination, flip transform, generates two shaders. The first appears to involve
+    //   gaussian_fp. The second is a long and general purpose shadow shader with a device space
+    //   transformation stage.
+    // The third combination is a cache hit, nothing new.
+    // The fourth combination, flip transform with a non-SRGB destination dataspace, is new.
+    //   It is unique in that nearly everything is done in the vertex shader, and that vertex shader
+    //   requires color correction. This is triggered differently from every other instance of color
+    //   correction. All other instances are triggered when src and dst dataspaces differ, while
+    //   this one is triggered by the destination being non-srgb. Apparently since the third
+    //   combination is a cache hit, this color correction is only added when the vertex shader is
+    //   doing something non-trivial.
+    for (auto transform : {mat4(), kFlip}) {
+        layer.geometry.positionTransform = transform;
+        caster.geometry.positionTransform = transform;
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                base::unique_fd(), &drawFence);
     }
 }
 
@@ -138,6 +141,7 @@
                                           }},
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
         layer.sourceDataspace = dataspace;
@@ -151,7 +155,7 @@
                 for (auto alpha : {half(.2f), half(1.0f)}) {
                     layer.alpha = alpha;
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd(), &drawFence);
                 }
             }
         }
@@ -174,13 +178,14 @@
             .alpha = 0.5,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                     base::unique_fd(), nullptr);
+                                     base::unique_fd(), &drawFence);
         }
     }
 }
@@ -199,12 +204,13 @@
             .skipContentDraw = true,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                 base::unique_fd(), nullptr);
+                                 base::unique_fd(), &drawFence);
     }
 }
 
@@ -240,6 +246,7 @@
                     },
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
         layer.source = pixelSource;
@@ -251,7 +258,7 @@
                 for (float alpha : {0.5f, 1.f}) {
                     layer.alpha = alpha,
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd(), &drawFence);
                 }
             }
         }
@@ -287,9 +294,10 @@
 
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                             base::unique_fd(), nullptr);
+                             base::unique_fd(), &drawFence);
 }
 
 static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -316,9 +324,10 @@
 
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                            base::unique_fd(), nullptr);
+                            base::unique_fd(), &drawFence);
 }
 
 //
@@ -381,6 +390,7 @@
                                                           ExternalTexture::Usage::WRITEABLE);
         drawHolePunchLayer(renderengine, display, dstTexture);
         drawSolidLayers(renderengine, display, dstTexture);
+
         drawShadowLayers(renderengine, display, srcTexture);
         drawShadowLayers(renderengine, p3Display, srcTexture);
 
@@ -421,6 +431,14 @@
 
         drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
 
+        // draw one final layer synchronously to force GL submit
+        LayerSettings layer{
+                .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
+        };
+        auto layers = std::vector<const LayerSettings*>{&layer};
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                             base::unique_fd(), nullptr); // null drawFence makes it synchronous
+
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
         const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 16b5884..9fbbdc3 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1127,6 +1127,73 @@
     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 }
 
+/**
+ *  Verifies that common, simple bounds + clip combinations can be converted into
+ *  a single RRect draw call returning true if possible. If true the radii parameter
+ *  will be filled with the correct radii values that combined with bounds param will
+ *  produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+                                    const SkRect& insetCrop, float cornerRadius,
+                                    SkVector radii[4]) {
+    const bool leftEqual = bounds.fLeft == crop.fLeft;
+    const bool topEqual = bounds.fTop == crop.fTop;
+    const bool rightEqual = bounds.fRight == crop.fRight;
+    const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+    // In the event that the corners of the bounds only partially align with the crop we
+    // need to ensure that the resulting shape can still be represented as a round rect.
+    // In particular the round rect implementation will scale the value of all corner radii
+    // if the sum of the radius along any edge is greater than the length of that edge.
+    // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+    const bool requiredWidth = bounds.width() > (cornerRadius * 2);
+    const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+    if (!requiredWidth || !requiredHeight) {
+        return false;
+    }
+
+    // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+    // contained within the cropped shape and does not need rounded.
+    // compute the UpperLeft corner radius
+    if (leftEqual && topEqual) {
+        radii[0].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[0].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the UpperRight corner radius
+    if (rightEqual && topEqual) {
+        radii[1].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[1].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomRight corner radius
+    if (rightEqual && bottomEqual) {
+        radii[2].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[2].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomLeft corner radius
+    if (leftEqual && bottomEqual) {
+        radii[3].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[3].set(0, 0);
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
                                                                         const FloatRect& cropRect,
                                                                         const float cornerRadius) {
@@ -1144,66 +1211,20 @@
         // converting them to a single RRect draw. It is possible there are other cases
         // that can be converted.
         if (crop.contains(bounds)) {
-            bool intersectionIsRoundRect = true;
-            // check each cropped corner to ensure that it exactly matches the crop or is full
-            SkVector radii[4];
-
             const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
-
-            const bool leftEqual = bounds.fLeft == crop.fLeft;
-            const bool topEqual = bounds.fTop == crop.fTop;
-            const bool rightEqual = bounds.fRight == crop.fRight;
-            const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
-            // compute the UpperLeft corner radius
-            if (leftEqual && topEqual) {
-                radii[0].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fTop)) {
-                radii[0].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the UpperRight corner radius
-            if (rightEqual && topEqual) {
-                radii[1].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fTop)) {
-                radii[1].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomRight corner radius
-            if (rightEqual && bottomEqual) {
-                radii[2].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fBottom)) {
-                radii[2].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomLeft corner radius
-            if (leftEqual && bottomEqual) {
-                radii[3].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
-                radii[3].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
+            if (insetCrop.contains(bounds)) {
+                return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
             }
 
-            if (intersectionIsRoundRect) {
+            SkVector radii[4];
+            if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
                 SkRRect intersectionBounds;
                 intersectionBounds.setRectRadii(bounds, radii);
                 return {intersectionBounds, clip};
             }
         }
 
-        // we didn't it any of our fast paths so set the clip to the cropRect
+        // we didn't hit any of our fast paths so set the clip to the cropRect
         clip.setRectXY(crop, cornerRadius, cornerRadius);
     }
 
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index ec47f96..33053a0 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1869,6 +1869,40 @@
     expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
+    redLayer.geometry.roundedCornersRadius = 64;
+    redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(&redLayer);
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the top corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+
+    // ensure that the entire height of the red layer was clipped by the rounded corners crop.
+    expectBufferColor(Point(0, 31), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0);
+
+    // the bottom middle should be red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 5a118ec..ba5a64f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -158,7 +158,7 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
@@ -175,7 +175,7 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libgralloctypes",
     ],
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 24d186c..73e5749 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -96,6 +96,7 @@
         "libinputflinger_base",
         "libinputreporter",
         "libinputreader",
+        "libgui",
     ],
     static_libs: [
         "libinputdispatcher",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e81dcfe..7b3658d 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -114,33 +114,6 @@
     return mDispatcher;
 }
 
-class BinderWindowHandle : public WindowInfoHandle {
-public:
-    BinderWindowHandle(const WindowInfo& info) { mInfo = info; }
-
-    bool updateInfo() override {
-        return true;
-    }
-};
-
-binder::Status InputManager::setInputWindows(
-        const std::vector<WindowInfo>& infos,
-        const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
-
-    std::vector<sp<WindowInfoHandle>> handles;
-    for (const auto& info : infos) {
-        handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
-        handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
-    }
-    mDispatcher->setInputWindows(handlesPerDisplay);
-
-    if (setInputWindowsListener) {
-        setInputWindowsListener->onSetInputWindowsFinished();
-    }
-    return binder::Status::ok();
-}
-
 // Used by tests only.
 binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
     IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 035a9a3..4c07c22 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,7 +26,6 @@
 
 #include <InputDispatcherInterface.h>
 #include <InputDispatcherPolicyInterface.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 
@@ -38,7 +37,6 @@
 #include <utils/Vector.h>
 
 using android::os::BnInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 namespace android {
 class InputChannel;
@@ -104,10 +102,6 @@
     sp<InputDispatcherInterface> getDispatcher() override;
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setInputWindows(
-            const std::vector<gui::WindowInfo>& handles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
-
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
     binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
     binder::Status setFocusedWindow(const gui::FocusRequest&) override;
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index f77c029..5e9facf 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -185,10 +185,11 @@
                      const sp<InputDispatcher>& dispatcher, const std::string name)
           : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
         inputApplicationHandle->updateInfo();
+        updateInfo();
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
 
-    virtual bool updateInfo() override {
+    void updateInfo() {
         mInfo.token = mClientChannel->getConnectionToken();
         mInfo.name = "FakeWindowHandle";
         mInfo.type = WindowInfo::Type::APPLICATION;
@@ -207,8 +208,6 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = ADISPLAY_ID_DEFAULT;
-
-        return true;
     }
 
 protected:
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 7c7a16b..171f2b5 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -64,6 +64,7 @@
         "libstatspull",
         "libstatssocket",
         "libui",
+        "libgui",
         "libutils",
         "lib-platform-compat-native-api",
         "server_configurable_flags",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 8f4527d..288068f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -319,17 +319,4 @@
     return seq;
 }
 
-// --- CommandEntry ---
-
-CommandEntry::CommandEntry(Command command)
-      : command(command),
-        eventTime(0),
-        keyEntry(nullptr),
-        userActivityEventType(0),
-        seq(0),
-        handled(false),
-        enabled(false) {}
-
-CommandEntry::~CommandEntry() {}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 1b7fcf2..8feb982 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -245,55 +245,6 @@
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
 VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
 
-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;
-    std::shared_ptr<KeyEntry> keyEntry;
-    std::shared_ptr<SensorEntry> sensorEntry;
-    std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
-    std::string reason;
-    int32_t userActivityEventType;
-    uint32_t seq;
-    bool handled;
-    sp<IBinder> connectionToken;
-    sp<IBinder> oldToken;
-    sp<IBinder> newToken;
-    std::string obscuringPackage;
-    bool enabled;
-    int32_t pid;
-    nsecs_t consumeTime; // time when the event was consumed by InputConsumer
-    int32_t displayId;
-    float x;
-    float y;
-};
-
 } // namespace android::inputdispatcher
 
 #endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 07b4f30..1478a3c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -19,34 +19,6 @@
 
 #define LOG_NDEBUG 1
 
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 0
-
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about channel creation
-#define DEBUG_CHANNEL_CREATION 0
-
-// Log debug messages about input event injection.
-#define DEBUG_INJECTION 0
-
-// Log debug messages about input focus tracking.
-static constexpr bool DEBUG_FOCUS = false;
-
-// Log debug messages about touch occlusion
-// STOPSHIP(b/169067926): Set to false
-static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
-
-// Log debug messages about the app switch latency optimization.
-#define DEBUG_APP_SWITCH 0
-
-// Log debug messages about hover events.
-#define DEBUG_HOVER 0
-
 #include <InputFlingerProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
@@ -55,6 +27,7 @@
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
 #include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <gui/SurfaceComposerClient.h>
 #include <input/InputDevice.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
@@ -93,9 +66,50 @@
 
 namespace android::inputdispatcher {
 
+namespace {
+
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+
+// Log debug messages about the dispatch cycle.
+constexpr bool DEBUG_DISPATCH_CYCLE = false;
+
+// Log debug messages about channel creation
+constexpr bool DEBUG_CHANNEL_CREATION = false;
+
+// Log debug messages about input event injection.
+constexpr bool DEBUG_INJECTION = false;
+
+// Log debug messages about input focus tracking.
+constexpr bool DEBUG_FOCUS = false;
+
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
+// Log debug messages about the app switch latency optimization.
+constexpr bool DEBUG_APP_SWITCH = false;
+
+// Log debug messages about hover events.
+constexpr bool DEBUG_HOVER = false;
+
+// Temporarily releases a held mutex for the lifetime of the instance.
+// Named to match std::scoped_lock
+class scoped_unlock {
+public:
+    explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
+    ~scoped_unlock() { mMutex.lock(); }
+
+private:
+    std::mutex& mMutex;
+};
+
 // When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
 // coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-static bool isPerWindowInputRotationEnabled() {
+bool isPerWindowInputRotationEnabled() {
     static const bool PER_WINDOW_INPUT_ROTATION =
             sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
 
@@ -135,27 +149,27 @@
 constexpr int LOGTAG_INPUT_INTERACTION = 62000;
 constexpr int LOGTAG_INPUT_FOCUS = 62001;
 
-static inline nsecs_t now() {
+inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-static inline const char* toString(bool value) {
+inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
 
-static inline const std::string toString(sp<IBinder> binder) {
+inline const std::string toString(const sp<IBinder>& binder) {
     if (binder == nullptr) {
         return "<null>";
     }
     return StringPrintf("%p", binder.get());
 }
 
-static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+inline int32_t getMotionEventActionPointerIndex(int32_t action) {
     return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
             AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
 }
 
-static bool isValidKeyAction(int32_t action) {
+bool isValidKeyAction(int32_t action) {
     switch (action) {
         case AKEY_EVENT_ACTION_DOWN:
         case AKEY_EVENT_ACTION_UP:
@@ -165,7 +179,7 @@
     }
 }
 
-static bool validateKeyEvent(int32_t action) {
+bool validateKeyEvent(int32_t action) {
     if (!isValidKeyAction(action)) {
         ALOGE("Key event has invalid action code 0x%x", action);
         return false;
@@ -173,7 +187,7 @@
     return true;
 }
 
-static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+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:
@@ -198,12 +212,12 @@
     }
 }
 
-static int64_t millis(std::chrono::nanoseconds t) {
+int64_t millis(std::chrono::nanoseconds t) {
     return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
 }
 
-static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
-                                const PointerProperties* pointerProperties) {
+bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+                         const PointerProperties* pointerProperties) {
     if (!isValidMotionAction(action, actionButton, pointerCount)) {
         ALOGE("Motion event has invalid action code 0x%x", action);
         return false;
@@ -230,7 +244,7 @@
     return true;
 }
 
-static std::string dumpRegion(const Region& region) {
+std::string dumpRegion(const Region& region) {
     if (region.isEmpty()) {
         return "<empty>";
     }
@@ -251,7 +265,7 @@
     return dump;
 }
 
-static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
     constexpr size_t maxEntries = 50; // max events to print
     constexpr size_t skipBegin = maxEntries / 2;
     const size_t skipEnd = queue.size() - maxEntries / 2;
@@ -290,12 +304,12 @@
  * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
  */
 template <typename K, typename V>
-static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+V getValueByKey(const std::unordered_map<K, V>& map, K key) {
     auto it = map.find(key);
     return it != map.end() ? it->second : V{};
 }
 
-static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
+bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
     if (first == second) {
         return true;
     }
@@ -307,7 +321,7 @@
     return first->getToken() == second->getToken();
 }
 
-static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
+bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
     if (first == nullptr || second == nullptr) {
         return false;
     }
@@ -315,13 +329,13 @@
             first->applicationInfo.token == second->applicationInfo.token;
 }
 
-static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
     return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
 }
 
-static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
-                                                          std::shared_ptr<EventEntry> eventEntry,
-                                                          int32_t inputTargetFlags) {
+std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+                                                   std::shared_ptr<EventEntry> eventEntry,
+                                                   int32_t inputTargetFlags) {
     if (eventEntry->type == EventEntry::Type::MOTION) {
         const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
         if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
@@ -398,9 +412,9 @@
     return dispatchEntry;
 }
 
-static void addGestureMonitors(const std::vector<Monitor>& monitors,
-                               std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
-                               float yOffset = 0) {
+void addGestureMonitors(const std::vector<Monitor>& monitors,
+                        std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+                        float yOffset = 0) {
     if (monitors.empty()) {
         return;
     }
@@ -410,9 +424,8 @@
     }
 }
 
-static status_t openInputChannelPair(const std::string& name,
-                                     std::shared_ptr<InputChannel>& serverChannel,
-                                     std::unique_ptr<InputChannel>& clientChannel) {
+status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel,
+                              std::unique_ptr<InputChannel>& clientChannel) {
     std::unique_ptr<InputChannel> uniqueServerChannel;
     status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
 
@@ -421,7 +434,7 @@
 }
 
 template <typename T>
-static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
     if (lhs == nullptr && rhs == nullptr) {
         return true;
     }
@@ -431,7 +444,7 @@
     return *lhs == *rhs;
 }
 
-static sp<IPlatformCompatNative> getCompatService() {
+sp<IPlatformCompatNative> getCompatService() {
     sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
     if (service == nullptr) {
         ALOGE("Failed to link to compat service");
@@ -440,7 +453,7 @@
     return interface_cast<IPlatformCompatNative>(service);
 }
 
-static KeyEvent createKeyEvent(const KeyEntry& entry) {
+KeyEvent createKeyEvent(const KeyEntry& entry) {
     KeyEvent event;
     event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
                      entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
@@ -448,7 +461,7 @@
     return event;
 }
 
-static std::optional<int32_t> findMonitorPidByToken(
+std::optional<int32_t> findMonitorPidByToken(
         const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
         const sp<IBinder>& token) {
     for (const auto& it : monitorsByDisplay) {
@@ -462,7 +475,7 @@
     return std::nullopt;
 }
 
-static bool shouldReportMetricsForConnection(const Connection& connection) {
+bool shouldReportMetricsForConnection(const Connection& connection) {
     // Do not keep track of gesture monitors. They receive every event and would disproportionately
     // affect the statistics.
     if (connection.monitor) {
@@ -475,8 +488,7 @@
     return true;
 }
 
-static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry,
-                                      const Connection& connection) {
+bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
     const EventEntry& eventEntry = *dispatchEntry.eventEntry;
     const int32_t& inputEventId = eventEntry.id;
     if (inputEventId != dispatchEntry.resolvedEventId) {
@@ -512,6 +524,22 @@
     return true;
 }
 
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+bool isConnectionResponsive(const Connection& connection) {
+    const nsecs_t currentTime = now();
+    for (const DispatchEntry* entry : connection.waitQueue) {
+        if (entry->timeoutTime < currentTime) {
+            return false;
+        }
+    }
+    return true;
+}
+
+} // namespace
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -545,20 +573,24 @@
 }
 
 InputDispatcher::~InputDispatcher() {
-    { // acquire lock
-        std::scoped_lock _l(mLock);
+    std::scoped_lock _l(mLock);
 
-        resetKeyRepeatLocked();
-        releasePendingEventLocked();
-        drainInboundQueueLocked();
-    }
+    resetKeyRepeatLocked();
+    releasePendingEventLocked();
+    drainInboundQueueLocked();
+    mCommandQueue.clear();
 
     while (!mConnectionsByToken.empty()) {
         sp<Connection> connection = mConnectionsByToken.begin()->second;
-        removeInputChannel(connection->inputChannel->getConnectionToken());
+        removeInputChannelLocked(connection->inputChannel->getConnectionToken(),
+                                 false /* notify */);
     }
 }
 
+void InputDispatcher::onFirstRef() {
+    SurfaceComposerClient::getDefault()->addWindowInfosListener(this);
+}
+
 status_t InputDispatcher::start() {
     if (mThread) {
         return ALREADY_EXISTS;
@@ -591,7 +623,7 @@
 
         // Run all pending commands if there are any.
         // If any commands were run then force the next poll to wake up immediately.
-        if (runCommandsLockedInterruptible()) {
+        if (runCommandsLockedInterruptable()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
 
@@ -951,9 +983,9 @@
                     mAppSwitchSawKeyDown = true;
                 } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                     if (mAppSwitchSawKeyDown) {
-#if DEBUG_APP_SWITCH
-                        ALOGD("App switch is pending!");
-#endif
+                        if (DEBUG_APP_SWITCH) {
+                            ALOGD("App switch is pending!");
+                        }
                         mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                         mAppSwitchSawKeyDown = false;
                         needWake = true;
@@ -1048,9 +1080,9 @@
     const char* reason;
     switch (dropReason) {
         case DropReason::POLICY:
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("Dropped event because policy consumed it.");
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("Dropped event because policy consumed it.");
+            }
             reason = "inbound event was dropped because the policy consumed it";
             break;
         case DropReason::DISABLED:
@@ -1134,37 +1166,35 @@
 void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
     mAppSwitchDueTime = LONG_LONG_MAX;
 
-#if DEBUG_APP_SWITCH
-    if (handled) {
-        ALOGD("App switch has arrived.");
-    } else {
-        ALOGD("App switch was abandoned.");
+    if (DEBUG_APP_SWITCH) {
+        if (handled) {
+            ALOGD("App switch has arrived.");
+        } else {
+            ALOGD("App switch was abandoned.");
+        }
     }
-#endif
 }
 
 bool InputDispatcher::haveCommandsLocked() const {
     return !mCommandQueue.empty();
 }
 
-bool InputDispatcher::runCommandsLockedInterruptible() {
+bool InputDispatcher::runCommandsLockedInterruptable() {
     if (mCommandQueue.empty()) {
         return false;
     }
 
     do {
-        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+        auto command = std::move(mCommandQueue.front());
         mCommandQueue.pop_front();
-        Command command = commandEntry->command;
-        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
-
-        commandEntry->connection.clear();
+        // Commands are run with the lock held, but may release and re-acquire the lock from within.
+        command();
     } while (!mCommandQueue.empty());
     return true;
 }
 
-void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
-    mCommandQueue.push_back(std::move(commandEntry));
+void InputDispatcher::postCommandLocked(Command&& command) {
+    mCommandQueue.push_back(command);
 }
 
 void InputDispatcher::drainInboundQueueLocked() {
@@ -1186,9 +1216,9 @@
 void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
     InjectionState* injectionState = entry->injectionState;
     if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("Injected inbound event was dropped.");
-#endif
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("Injected inbound event was dropped.");
+        }
         setInjectionResult(*entry, InputEventInjectionResult::FAILED);
     }
     if (entry == mNextUnblockedEvent) {
@@ -1223,27 +1253,28 @@
 
 bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
                                                          const ConfigurationChangedEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
+    }
 
     // Reset key repeating in case a keyboard device was added or removed or something.
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
-    commandEntry->eventTime = entry.eventTime;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyConfigurationChanged(eventTime);
+    };
+    postCommandLocked(std::move(command));
     return true;
 }
 
 bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
                                                 const DeviceResetEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
-          entry.deviceId);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+              entry.deviceId);
+    }
 
     // Reset key repeating in case a keyboard device was disabled or enabled.
     if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1384,9 +1415,9 @@
         } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
                    mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
             // The key on device 'deviceId' is still down, do not stop key repeat
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+            }
         } else if (!entry->syntheticRepeat) {
             resetKeyRepeatLocked();
         }
@@ -1417,13 +1448,13 @@
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<IBinder> focusedWindowToken =
                     mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
-            commandEntry->connectionToken = focusedWindowToken;
-            commandEntry->keyEntry = entry;
-            postCommandLocked(std::move(commandEntry));
+
+            auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
+                doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
+            };
+            postCommandLocked(std::move(command));
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1465,47 +1496,42 @@
 }
 
 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);
-#endif
-}
-
-void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
-    if (entry->accuracyChanged) {
-        mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+    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);
     }
-    mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
-                               entry->hwTimestamp, entry->values);
-    mLock.lock();
 }
 
-void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
+                                           const std::shared_ptr<SensorEntry>& entry,
                                            DropReason* dropReason, nsecs_t* nextWakeupTime) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
-          "source=0x%x, sensorType=%s",
-          entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
-          NamedEnum::string(entry->sensorType).c_str());
-#endif
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
-    commandEntry->sensorEntry = entry;
-    postCommandLocked(std::move(commandEntry));
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+              "source=0x%x, sensorType=%s",
+              entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+              NamedEnum::string(entry->sensorType).c_str());
+    }
+    auto command = [this, entry]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+
+        if (entry->accuracyChanged) {
+            mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+        }
+        mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+                                   entry->hwTimestamp, entry->values);
+    };
+    postCommandLocked(std::move(command));
 }
 
 bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
-          NamedEnum::string(sensorType).c_str());
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+              NamedEnum::string(sensorType).c_str());
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -1616,42 +1642,43 @@
 }
 
 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);
+    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);
 
-    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));
+        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));
+        }
     }
-#endif
 }
 
 void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                           std::shared_ptr<EventEntry> eventEntry,
                                           const std::vector<InputTarget>& inputTargets) {
     ATRACE_CALL();
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("dispatchEventToCurrentInputTargets");
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("dispatchEventToCurrentInputTargets");
+    }
 
     updateInteractionTokensLocked(*eventEntry, inputTargets);
 
@@ -2015,7 +2042,7 @@
                         ALOGD("%s", log.c_str());
                     }
                 }
-                onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+                sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
                 if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
                     ALOGW("Dropping untrusted touch event due to %s/%d",
                           occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
@@ -2132,10 +2159,10 @@
         if (mLastHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
              mLastHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover exit event to window %s.",
-                  mLastHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover exit event to window %s.",
+                      mLastHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
         }
@@ -2145,10 +2172,10 @@
         if (newHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
              newHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover enter event to window %s.",
-                  newHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover enter event to window %s.",
+                      newHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
                                              BitSet32(0));
@@ -2341,9 +2368,9 @@
                                       false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
-        notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+        sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
     } else {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
     }
     mDragState.reset();
 }
@@ -2389,7 +2416,7 @@
     } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
         finishDragAndDrop(entry.displayId, x, y);
     } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
         mDragState.reset();
     }
 }
@@ -2677,9 +2704,9 @@
     if (focusedWindowHandle != nullptr) {
         const WindowInfo* info = focusedWindowHandle->getInfo();
         if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
-#if DEBUG_DISPATCH_CYCLE
-            ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
-#endif
+            if (DEBUG_DISPATCH_CYCLE) {
+                ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
+            }
             return;
         }
     }
@@ -2717,12 +2744,12 @@
         }
     }
 
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
-    commandEntry->eventTime = eventEntry.eventTime;
-    commandEntry->userActivityEventType = eventType;
-    commandEntry->displayId = displayId;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
+                           REQUIRES(mLock) {
+                               scoped_unlock unlock(mLock);
+                               mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+                           };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
@@ -2735,21 +2762,21 @@
                              connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-          "globalScaleFactor=%f, pointerIds=0x%x %s",
-          connection->getInputChannelName().c_str(), inputTarget.flags,
-          inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
-          inputTarget.getPointerInfoString().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+              "globalScaleFactor=%f, pointerIds=0x%x %s",
+              connection->getInputChannelName().c_str(), inputTarget.flags,
+              inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+              inputTarget.getPointerInfoString().c_str());
+    }
 
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     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());
-#endif
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
+                  connection->getInputChannelName().c_str(), connection->getStatusLabel());
+        }
         return;
     }
 
@@ -2848,10 +2875,11 @@
 
             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());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
             break;
@@ -2881,11 +2909,11 @@
             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());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
+                          "enter event",
+                          connection->getInputChannelName().c_str());
+                }
                 // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
                 // this is a one-to-one event conversion.
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
@@ -2901,11 +2929,11 @@
 
             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());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
 
@@ -3037,10 +3065,11 @@
         return;
     }
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
-    commandEntry->newToken = token;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, token]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->onPointerDownOutsideFocus(token);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3050,9 +3079,9 @@
                                            connection->getInputChannelName().c_str());
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
+    }
 
     while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
         DispatchEntry* dispatchEntry = connection->outboundQueue.front();
@@ -3190,11 +3219,11 @@
                 } 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());
-#endif
+                    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());
+                    }
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -3261,10 +3290,10 @@
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
                                                 bool handled, nsecs_t consumeTime) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
-          connection->getInputChannelName().c_str(), seq, toString(handled));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+              connection->getInputChannelName().c_str(), seq, toString(handled));
+    }
 
     if (connection->status == Connection::STATUS_BROKEN ||
         connection->status == Connection::STATUS_ZOMBIE) {
@@ -3272,16 +3301,19 @@
     }
 
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
+    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
+        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
                                                      const sp<Connection>& connection,
                                                      bool notify) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
-          connection->getInputChannelName().c_str(), toString(notify));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+              connection->getInputChannelName().c_str(), toString(notify));
+    }
 
     // Clear the dispatch queues.
     drainDispatchQueue(connection->outboundQueue);
@@ -3296,7 +3328,15 @@
 
         if (notify) {
             // Notify other system components.
-            onDispatchCycleBrokenLocked(currentTime, connection);
+            ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+                  connection->getInputChannelName().c_str());
+
+            auto command = [this, connection]() REQUIRES(mLock) {
+                if (connection->status == Connection::STATUS_ZOMBIE) return;
+                scoped_unlock unlock(mLock);
+                mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+            };
+            postCommandLocked(std::move(command));
         }
     }
 }
@@ -3363,7 +3403,7 @@
             gotOne = true;
         }
         if (gotOne) {
-            runCommandsLockedInterruptible();
+            runCommandsLockedInterruptable();
             if (status == WOULD_BLOCK) {
                 return 1;
             }
@@ -3440,12 +3480,12 @@
     if (cancelationEvents.empty()) {
         return;
     }
-#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);
-#endif
+    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);
+    }
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3509,10 +3549,10 @@
         return;
     }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
         ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
               connection->getInputChannelName().c_str(), downEvents.size());
-#endif
+    }
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3655,9 +3695,9 @@
 }
 
 void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
+    }
 
     bool needWake;
     { // acquire lock
@@ -3711,14 +3751,14 @@
 }
 
 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);
-#endif
+    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);
+    }
     if (!validateKeyEvent(args->action)) {
         return;
     }
@@ -3789,33 +3829,33 @@
 }
 
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" 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, xCursorPosition=%f, "
-          "yCursorPosition=%f, downTime=%" PRId64,
-          args->id, args->eventTime, args->deviceId, args->source, args->displayId,
-          args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
-          args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
-          args->xCursorPosition, 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));
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" 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, xCursorPosition=%f, "
+              "yCursorPosition=%f, downTime=%" PRId64,
+              args->id, args->eventTime, args->deviceId, args->source, args->displayId,
+              args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
+              args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
+              args->xCursorPosition, 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));
+        }
     }
-#endif
     if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
                              args->pointerProperties)) {
         return;
@@ -3885,12 +3925,12 @@
 }
 
 void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
-          " sensorType=%s",
-          args->id, args->eventTime, args->deviceId, args->source,
-          NamedEnum::string(args->sensorType).c_str());
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+              " sensorType=%s",
+              args->id, args->eventTime, args->deviceId, args->source,
+              NamedEnum::string(args->sensorType).c_str());
+    }
 
     bool needWake;
     { // acquire lock
@@ -3913,10 +3953,10 @@
 }
 
 void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
-          args->deviceId, args->isOn);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
+              args->deviceId, args->isOn);
+    }
     mPolicy->notifyVibratorState(args->deviceId, args->isOn);
 }
 
@@ -3925,11 +3965,11 @@
 }
 
 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);
-#endif
+    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);
+    }
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
@@ -3937,10 +3977,10 @@
 }
 
 void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
-          args->deviceId);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+              args->deviceId);
+    }
 
     bool needWake;
     { // acquire lock
@@ -3957,10 +3997,10 @@
 }
 
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-          args->enabled ? "true" : "false");
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+              args->enabled ? "true" : "false");
+    }
 
     bool needWake;
     { // acquire lock
@@ -3978,11 +4018,11 @@
 InputEventInjectionResult InputDispatcher::injectInputEvent(
         const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
         InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-          "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
-          event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+              "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+              event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+    }
     nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
     policyFlags |= POLICY_FLAG_INJECTED;
@@ -4160,10 +4200,10 @@
 
                 nsecs_t remainingTimeout = endTime - now();
                 if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Timed out waiting for injection result "
-                          "to become available.");
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Timed out waiting for injection result "
+                              "to become available.");
+                    }
                     injectionResult = InputEventInjectionResult::TIMED_OUT;
                     break;
                 }
@@ -4174,16 +4214,16 @@
             if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
                 syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
                 while (injectionState->pendingForegroundDispatches != 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                          injectionState->pendingForegroundDispatches);
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+                              injectionState->pendingForegroundDispatches);
+                    }
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                        ALOGD("injectInputEvent - Timed out waiting for pending foreground "
-                              "dispatches to finish.");
-#endif
+                        if (DEBUG_INJECTION) {
+                            ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+                                  "dispatches to finish.");
+                        }
                         injectionResult = InputEventInjectionResult::TIMED_OUT;
                         break;
                     }
@@ -4196,10 +4236,10 @@
         injectionState->release();
     } // release lock
 
-#if DEBUG_INJECTION
-    ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
-          injectionResult, injectorPid, injectorUid);
-#endif
+    if (DEBUG_INJECTION) {
+        ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
+              injectionResult, injectorPid, injectorUid);
+    }
 
     return injectionResult;
 }
@@ -4246,11 +4286,11 @@
                                          InputEventInjectionResult injectionResult) {
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
-#if DEBUG_INJECTION
-        ALOGD("Setting input event injection result to %d.  "
-              "injectorPid=%d, injectorUid=%d",
-              injectionResult, injectionState->injectorPid, injectionState->injectorUid);
-#endif
+        if (DEBUG_INJECTION) {
+            ALOGD("Setting input event injection result to %d.  "
+                  "injectorPid=%d, injectorUid=%d",
+                  injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+        }
 
         if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
@@ -4409,11 +4449,6 @@
 
     std::vector<sp<WindowInfoHandle>> newHandles;
     for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
-        if (!handle->updateInfo()) {
-            // handle no longer valid
-            continue;
-        }
-
         const WindowInfo* info = handle->getInfo();
         if (getInputChannelLocked(handle->getToken()) == nullptr) {
             const bool noInputChannel =
@@ -4660,7 +4695,7 @@
 
             // Find new focused window and validate
             sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
-            notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
+            sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
             if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
@@ -5092,6 +5127,12 @@
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
+    if (!mCommandQueue.empty()) {
+        dump += StringPrintf(INDENT "CommandQueue: size=%zu\n", mCommandQueue.size());
+    } else {
+        dump += INDENT "CommandQueue: <empty>\n";
+    }
+
     if (!mConnectionsByToken.empty()) {
         dump += INDENT "Connections:\n";
         for (const auto& [token, connection] : mConnectionsByToken) {
@@ -5158,9 +5199,9 @@
 };
 
 Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
-#if DEBUG_CHANNEL_CREATION
-    ALOGD("channel '%s' ~ createInputChannel", name.c_str());
-#endif
+    if (DEBUG_CHANNEL_CREATION) {
+        ALOGD("channel '%s' ~ createInputChannel", name.c_str());
+    }
 
     std::unique_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
@@ -5441,46 +5482,92 @@
     mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-                                                    const sp<Connection>& connection, uint32_t seq,
-                                                    bool handled, nsecs_t consumeTime) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
-    commandEntry->connection = connection;
-    commandEntry->eventTime = currentTime;
-    commandEntry->seq = seq;
-    commandEntry->handled = handled;
-    commandEntry->consumeTime = consumeTime;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
+                                                     const sp<Connection>& connection, uint32_t seq,
+                                                     bool handled, nsecs_t consumeTime) {
+    // Handle post-event policy actions.
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt == connection->waitQueue.end()) {
+        return;
+    }
+    DispatchEntry* dispatchEntry = *dispatchEntryIt;
+    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
+    }
+    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
+        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
+                                           connection->inputChannel->getConnectionToken(),
+                                           dispatchEntry->deliveryTime, consumeTime, finishTime);
+    }
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent =
+                afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt != connection->waitQueue.end()) {
+        dispatchEntry = *dispatchEntryIt;
+        connection->waitQueue.erase(dispatchEntryIt);
+        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
+        if (!connection->responsive) {
+            connection->responsive = isConnectionResponsive(*connection);
+            if (connection->responsive) {
+                // The connection was unresponsive, and now it's responsive.
+                processConnectionResponsiveLocked(*connection);
+            }
+        }
+        traceWaitQueueLength(*connection);
+        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+            connection->outboundQueue.push_front(dispatchEntry);
+            traceOutboundQueueLength(*connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), 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());
-
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
-    commandEntry->connection = connection;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
+                                                    const sp<IBinder>& newToken) {
+    auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyFocusChanged(oldToken, newToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
-                                               const sp<IBinder>& newToken) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
-    commandEntry->oldToken = oldToken;
-    commandEntry->newToken = newToken;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
+    auto command = [this, token, x, y]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyDropWindow(token, x, y);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
-    commandEntry->newToken = token;
-    commandEntry->x = x;
-    commandEntry->y = y;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) {
+    auto command = [this, obscuringPackage]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyUntrustedTouch(obscuringPackage);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
@@ -5523,17 +5610,11 @@
             StringPrintf("%s does not have a focused window", application->getName().c_str());
     updateLastAnrStateLocked(*application, reason);
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = std::move(application);
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
-    commandEntry->obscuringPackage = obscuringPackage;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyNoFocusedWindowAnr(application);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,
@@ -5564,109 +5645,24 @@
     dumpDispatchStateLocked(mLastAnrState);
 }
 
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
-
-        mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
-
-        mLock.lock();
-    }
-}
-
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> oldToken = commandEntry->oldToken;
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyFocusChanged(oldToken, newToken);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorResponsive(commandEntry->pid);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
-        CommandEntry* commandEntry) {
-    KeyEntry& entry = *(commandEntry->keyEntry);
-    KeyEvent event = createKeyEvent(entry);
-
-    mLock.unlock();
-
-    android::base::Timer t;
-    const sp<IBinder>& token = commandEntry->connectionToken;
-    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());
-    }
-
-    mLock.lock();
+void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                             KeyEntry& entry) {
+    const KeyEvent event = createKeyEvent(entry);
+    nsecs_t delay = 0;
+    { // release lock
+        scoped_unlock unlock(mLock);
+        android::base::Timer t;
+        delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &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());
+        }
+    } // acquire lock
 
     if (delay < 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
-    } else if (!delay) {
+    } else if (delay == 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
     } else {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
@@ -5674,122 +5670,37 @@
     }
 }
 
-void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-    mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
-    mLock.lock();
-}
-
-/**
- * Connection is responsive if it has no events in the waitQueue that are older than the
- * current time.
- */
-static bool isConnectionResponsive(const Connection& connection) {
-    const nsecs_t currentTime = now();
-    for (const DispatchEntry* entry : connection.waitQueue) {
-        if (entry->timeoutTime < currentTime) {
-            return false;
-        }
-    }
-    return true;
-}
-
-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<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt == connection->waitQueue.end()) {
-        return;
-    }
-    DispatchEntry* dispatchEntry = *dispatchEntryIt;
-    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
-              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
-    }
-    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
-        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
-                                           connection->inputChannel->getConnectionToken(),
-                                           dispatchEntry->deliveryTime, commandEntry->consumeTime,
-                                           finishTime);
-    }
-
-    bool restartEvent;
-    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
-        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent =
-                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
-    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
-        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
-                                                           handled);
-    } else {
-        restartEvent = false;
-    }
-
-    // Dequeue the event and start the next cycle.
-    // Because the lock might have been released, it is possible that the
-    // contents of the wait queue to have been drained, so we need to double-check
-    // a few things.
-    dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt != connection->waitQueue.end()) {
-        dispatchEntry = *dispatchEntryIt;
-        connection->waitQueue.erase(dispatchEntryIt);
-        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
-        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
-        if (!connection->responsive) {
-            connection->responsive = isConnectionResponsive(*connection);
-            if (connection->responsive) {
-                // The connection was unresponsive, and now it's responsive.
-                processConnectionResponsiveLocked(*connection);
-            }
-        }
-        traceWaitQueueLength(*connection);
-        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-            connection->outboundQueue.push_front(dispatchEntry);
-            traceOutboundQueueLength(*connection);
-        } else {
-            releaseDispatchEntry(dispatchEntry);
-        }
-    }
-
-    // Start the next dispatch cycle for this connection.
-    startDispatchCycleLocked(now(), connection);
-}
-
 void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
-    std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
-    monitorUnresponsiveCommand->pid = pid;
-    monitorUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(monitorUnresponsiveCommand));
+    auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorUnresponsive(pid, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
                                                           std::string reason) {
-    std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
-    windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
-    windowUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(windowUnresponsiveCommand));
+    auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowUnresponsive(token, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
-    std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
-    monitorResponsiveCommand->pid = pid;
-    postCommandLocked(std::move(monitorResponsiveCommand));
+    auto command = [this, pid]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorResponsive(pid);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
-    std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
-    windowResponsiveCommand->connectionToken = std::move(connectionToken);
-    postCommandLocked(std::move(windowResponsiveCommand));
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
+    auto command = [this, connectionToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowResponsive(connectionToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
 /**
@@ -5837,7 +5748,7 @@
     sendWindowResponsiveCommandLocked(connectionToken);
 }
 
-bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                                        DispatchEntry* dispatchEntry,
                                                        KeyEntry& keyEntry, bool handled) {
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
@@ -5862,11 +5773,12 @@
         // then cancel the associated fallback key, if any.
         if (fallbackKeyCode != -1) {
             // 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);
-#endif
+            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);
+            }
             KeyEvent event = createKeyEvent(keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
@@ -5894,21 +5806,21 @@
         // Then ask the policy what to do with it.
         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);
-#endif
+            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);
+            }
             return false;
         }
 
         // 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);
-#endif
+        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);
+        }
         KeyEvent event = createKeyEvent(keyEntry);
 
         mLock.unlock();
@@ -5942,19 +5854,19 @@
         // longer dispatch a fallback key to the application.
         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);
-            } 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);
+            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);
+                } 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);
+                }
             }
-#endif
 
             CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                        "canceling fallback, policy no longer desires it");
@@ -5968,18 +5880,18 @@
             }
         }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        {
-            std::string msg;
-            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));
+        if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+            {
+                std::string msg;
+                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));
+                }
+                ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+                      fallbackKeys.size(), msg.c_str());
             }
-            ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
-                  fallbackKeys.size(), msg.c_str());
         }
-#endif
 
         if (fallback) {
             // Restart the dispatch cycle using the fallback key.
@@ -5995,16 +5907,16 @@
             keyEntry.downTime = event.getDownTime();
             keyEntry.syntheticRepeat = false;
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Dispatching fallback key.  "
-                  "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                  originalKeyCode, fallbackKeyCode, keyEntry.metaState);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Dispatching fallback key.  "
+                      "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+                      originalKeyCode, fallbackKeyCode, keyEntry.metaState);
+            }
             return true; // restart the event
         } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: No fallback key.");
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: No fallback key.");
+            }
 
             // Report the key as unhandled, since there is no fallback key.
             mReporter->reportUnhandledKey(keyEntry.id);
@@ -6013,21 +5925,12 @@
     return false;
 }
 
-bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                                           DispatchEntry* dispatchEntry,
                                                           MotionEntry& motionEntry, bool handled) {
     return false;
 }
 
-void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
-                              commandEntry->displayId);
-
-    mLock.lock();
-}
-
 void InputDispatcher::traceInboundQueueLengthLocked() {
     if (ATRACE_ENABLED()) {
         ATRACE_INT("iq", mInboundQueue.size());
@@ -6139,7 +6042,7 @@
     disablePointerCaptureForcedLocked();
 
     if (mFocusedDisplayId == changes.displayId) {
-        notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
+        sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
     }
 }
 
@@ -6173,19 +6076,11 @@
 }
 
 void InputDispatcher::setPointerCaptureLocked(bool enabled) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doSetPointerCaptureLockedInterruptible);
-    commandEntry->enabled = enabled;
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::doSetPointerCaptureLockedInterruptible(
-        android::inputdispatcher::CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->setPointerCapture(commandEntry->enabled);
-
-    mLock.lock();
+    auto command = [this, enabled]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->setPointerCapture(enabled);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::displayRemoved(int32_t displayId) {
@@ -6203,4 +6098,16 @@
     mLooper->wake();
 }
 
+void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) {
+    // The listener sends the windows as a flattened array. Separate the windows by display for
+    // more convenient parsing.
+    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
+
+    for (const auto& info : windowInfos) {
+        handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
+        handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
+    }
+    setInputWindows(handlesPerDisplay);
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 32a467e..6df333a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -58,6 +58,7 @@
 
 #include <InputListener.h>
 #include <InputReporterInterface.h>
+#include <gui/WindowInfosListener.h>
 
 namespace android::inputdispatcher {
 
@@ -80,7 +81,7 @@
  *
  *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
  */
-class InputDispatcher : public android::InputDispatcherInterface {
+class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener {
 protected:
     ~InputDispatcher() override;
 
@@ -142,6 +143,8 @@
 
     void displayRemoved(int32_t displayId) override;
 
+    void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
@@ -168,7 +171,26 @@
     std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
-    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+    // 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 called with the lock held, but they can release and re-acquire the lock from
+    // within.
+    using Command = std::function<void()>;
+    std::deque<Command> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
 
@@ -292,8 +314,8 @@
 
     // Deferred command processing.
     bool haveCommandsLocked() const REQUIRES(mLock);
-    bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+    bool runCommandsLockedInterruptable() REQUIRES(mLock);
+    void postCommandLocked(Command&& command) REQUIRES(mLock);
 
     nsecs_t processAnrsLocked() REQUIRES(mLock);
     std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -401,7 +423,7 @@
             DropReason& dropReason) REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+    void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
     void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
@@ -448,24 +470,11 @@
      */
     void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
 
-    /**
-     * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
-     */
     void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
-     */
-    void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+    void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
             REQUIRES(mLock);
-    /**
-     * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
-     */
     void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowResponsiveLockedInterruptible` command.
-     */
-    void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
-
+    void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
 
     // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
     // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -599,53 +608,30 @@
             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, nsecs_t consumeTime)
+    void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
+                                        uint32_t seq, bool handled, nsecs_t consumeTime)
             REQUIRES(mLock);
-    void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
-            REQUIRES(mLock);
+    void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                KeyEntry& entry) REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
-    void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+    void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
             REQUIRES(mLock);
-    void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
-    void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const InputApplicationHandle& application,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& 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 doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-
-    // ANR-related callbacks - start
-    void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    // ANR-related callbacks - end
-    void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                           DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
-    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
@@ -656,6 +642,8 @@
 
     sp<InputReporterInterface> mReporter;
     sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
+
+    void onFirstRef() override;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 80db035..a7dccf0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -21,7 +21,6 @@
 #include <android-base/result.h>
 #include <android/gui/FocusRequest.h>
 #include <android/os/BlockUntrustedTouchesMode.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <android/os/InputEventInjectionResult.h>
 #include <android/os/InputEventInjectionSync.h>
 #include <gui/InputApplication.h>
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index c05ff39..3cf1b2b 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -23,7 +23,6 @@
 #include "InputHost.h"
 
 #include <android/os/BnInputFlinger.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <binder/Binder.h>
 #include <cutils/compiler.h>
 #include <utils/String16.h>
@@ -31,9 +30,7 @@
 #include <utils/StrongPointer.h>
 
 using android::gui::FocusRequest;
-using android::gui::WindowInfo;
 using android::os::BnInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 namespace android {
 
@@ -46,10 +43,6 @@
     InputFlinger() ANDROID_API;
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setInputWindows(const std::vector<WindowInfo>&,
-                                   const sp<ISetInputWindowsListener>&) override {
-        return binder::Status::ok();
-    }
     binder::Status createInputChannel(const std::string&, InputChannel*) override {
         return binder::Status::ok();
     }
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 7ac2dec..a507632 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -303,7 +303,7 @@
      * the device
      */
     mDeviceEnabled = false;
-    for (const auto& [sensorType, sensor] : mSensors) {
+    for (const auto& [_, sensor] : mSensors) {
         // If any sensor is on we will turn on the device.
         if (sensor.enabled) {
             mDeviceEnabled = true;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 4c5ff63..7347b2c 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -33,9 +33,7 @@
 // projection are part of the input window's transform. This means InputReader should work in the
 // un-rotated coordinate space.
 static bool isPerWindowInputRotationEnabled() {
-    static const bool PER_WINDOW_INPUT_ROTATION =
-            sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
-    return PER_WINDOW_INPUT_ROTATION;
+    return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
 }
 
 static int32_t getInverseRotation(int32_t orientation) {
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 8c0edca..662be80 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -41,7 +41,6 @@
         mInfo.focusable = focusable;
     }
 
-    bool updateInfo() { return true; }
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
     void setVisible(bool visible) { mInfo.visible = visible; }
 };
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
index aff03bf..5aeb21f 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -17,14 +17,11 @@
 import android.InputChannel;
 import android.gui.FocusRequest;
 import android.gui.WindowInfo;
-import android.os.ISetInputWindowsListener;
 
 /** @hide */
 interface IInputFlingerQuery
 {
     /* Test interfaces */
-    void getInputWindows(out WindowInfo[] inputHandles);
     void getInputChannels(out InputChannel[] channels);
-    void getLastFocusRequest(out FocusRequest request);
     void resetInputManager();
 }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f03cf8b..50e1854 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -937,8 +937,6 @@
         mInfo.displayId = displayId;
     }
 
-    virtual bool updateInfo() { return true; }
-
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
 
     void setVisible(bool visible) { mInfo.visible = visible; }
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index d2a98df..454e531 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -18,9 +18,7 @@
 #include <IInputFlingerQuery.h>
 
 #include <android/os/BnInputFlinger.h>
-#include <android/os/BnSetInputWindowsListener.h>
 #include <android/os/IInputFlinger.h>
-#include <android/os/ISetInputWindowsListener.h>
 
 #include <binder/Binder.h>
 #include <binder/IPCThreadState.h>
@@ -28,7 +26,6 @@
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 
-#include <gui/WindowInfo.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 
@@ -45,57 +42,17 @@
 #define TAG "InputFlingerServiceTest"
 
 using android::gui::FocusRequest;
-using android::gui::WindowInfo;
-using android::gui::WindowInfoHandle;
 using android::os::BnInputFlinger;
-using android::os::BnSetInputWindowsListener;
 using android::os::IInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 using std::chrono_literals::operator""ms;
 using std::chrono_literals::operator""s;
 
 namespace android {
 
-static const sp<IBinder> TestInfoToken = new BBinder();
-static const sp<IBinder> FocusedTestInfoToken = new BBinder();
-static constexpr int32_t TestInfoId = 1;
-static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
-static constexpr Flags<WindowInfo::Flag> TestInfoFlags = WindowInfo::Flag::NOT_FOCUSABLE;
-static constexpr WindowInfo::Type TestInfoType = WindowInfo::Type::INPUT_METHOD;
-static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
-static constexpr int32_t TestInfoFrameLeft = 93;
-static constexpr int32_t TestInfoFrameTop = 34;
-static constexpr int32_t TestInfoFrameRight = 16;
-static constexpr int32_t TestInfoFrameBottom = 19;
-static constexpr int32_t TestInfoSurfaceInset = 17;
-static constexpr float TestInfoGlobalScaleFactor = 0.3;
-static constexpr float TestInfoWindowXScale = 0.4;
-static constexpr float TestInfoWindowYScale = 0.5;
-static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
-                                                 450 /* bottom */};
-static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
-static constexpr bool TestInfoVisible = false;
-static constexpr bool TestInfoTrustedOverlay = true;
-static constexpr bool TestInfoFocusable = false;
-static constexpr bool TestInfoHasWallpaper = false;
-static constexpr bool TestInfoPaused = false;
-static constexpr int32_t TestInfoOwnerPid = 19;
-static constexpr int32_t TestInfoOwnerUid = 24;
-static constexpr WindowInfo::Feature TestInfoInputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
-static constexpr int32_t TestInfoDisplayId = 34;
-static constexpr int32_t TestInfoPortalToDisplayId = 2;
-static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
-static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
-
-static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
-static const sp<IBinder> TestAppInfoToken = new BBinder();
-static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
-
 static const String16 kTestServiceName = String16("InputFlingerService");
 static const String16 kQueryServiceName = String16("InputFlingerQueryService");
 
-struct SetInputWindowsListener;
 // --- InputFlingerServiceTest ---
 class InputFlingerServiceTest : public testing::Test {
 public:
@@ -104,32 +61,15 @@
 
 protected:
     void InitializeInputFlinger();
-    void setInputWindowsByInfos(const std::vector<WindowInfo>& infos);
-    void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
-                          nsecs_t timestampNanos);
-
-    void setInputWindowsFinished();
-    void verifyInputWindowInfo(const WindowInfo& info) const;
-    WindowInfo& getInfo() const { return const_cast<WindowInfo&>(mInfo); }
 
     sp<IInputFlinger> mService;
     sp<IInputFlingerQuery> mQuery;
 
 private:
-    sp<SetInputWindowsListener> mSetInputWindowsListener;
     std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
-    WindowInfo mInfo;
     std::mutex mLock;
-    std::condition_variable mSetInputWindowsFinishedCondition;
 };
 
-struct SetInputWindowsListener : BnSetInputWindowsListener {
-    explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
-
-    binder::Status onSetInputWindowsFinished() override;
-
-    std::function<void()> mCbFunc;
-};
 
 class TestInputManager : public BnInputFlinger {
 protected:
@@ -138,16 +78,10 @@
 public:
     TestInputManager(){};
 
-    binder::Status getInputWindows(std::vector<WindowInfo>* inputHandles);
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
-    binder::Status getLastFocusRequest(FocusRequest*);
 
     status_t dump(int fd, const Vector<String16>& args) override;
 
-    binder::Status setInputWindows(
-            const std::vector<WindowInfo>& handles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
-
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
     binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
     binder::Status setFocusedWindow(const FocusRequest&) override;
@@ -156,62 +90,28 @@
 
 private:
     mutable Mutex mLock;
-    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> mHandlesPerDisplay;
     std::vector<std::shared_ptr<InputChannel>> mInputChannels;
-    FocusRequest mFocusRequest;
 };
 
 class TestInputQuery : public BnInputFlingerQuery {
 public:
     TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
-    binder::Status getInputWindows(std::vector<WindowInfo>* inputHandles) override;
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
-    binder::Status getLastFocusRequest(FocusRequest*) override;
     binder::Status resetInputManager() override;
 
 private:
     sp<android::TestInputManager> mManager;
 };
 
-binder::Status TestInputQuery::getInputWindows(std::vector<WindowInfo>* inputHandles) {
-    return mManager->getInputWindows(inputHandles);
-}
-
 binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
     return mManager->getInputChannels(channels);
 }
 
-binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
-    return mManager->getLastFocusRequest(request);
-}
-
 binder::Status TestInputQuery::resetInputManager() {
     mManager->reset();
     return binder::Status::ok();
 }
 
-binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
-    if (mCbFunc != nullptr) {
-        mCbFunc();
-    }
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::setInputWindows(
-        const std::vector<WindowInfo>& infos,
-        const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-    AutoMutex _l(mLock);
-
-    for (const auto& info : infos) {
-        mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
-        mHandlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
-    }
-    if (setInputWindowsListener) {
-        setInputWindowsListener->onSetInputWindowsFinished();
-    }
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::createInputChannel(const std::string& name,
                                                     InputChannel* outChannel) {
     AutoMutex _l(mLock);
@@ -249,15 +149,6 @@
     return NO_ERROR;
 }
 
-binder::Status TestInputManager::getInputWindows(std::vector<WindowInfo>* inputInfos) {
-    for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
-        for (auto& inputHandle : inputHandles) {
-            inputInfos->push_back(*inputHandle->getInfo());
-        }
-    }
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
     channels->clear();
     for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
@@ -266,64 +157,16 @@
     return binder::Status::ok();
 }
 
-binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
-    *request = mFocusRequest;
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
-    mFocusRequest = request;
     return binder::Status::ok();
 }
 
 void TestInputManager::reset() {
-    mHandlesPerDisplay.clear();
     mInputChannels.clear();
-    mFocusRequest = FocusRequest();
 }
 
 void InputFlingerServiceTest::SetUp() {
-    mSetInputWindowsListener = new SetInputWindowsListener([&]() {
-        std::unique_lock<std::mutex> lock(mLock);
-        mSetInputWindowsFinishedCondition.notify_all();
-    });
     InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-
-    mInfo.token = TestInfoToken;
-    mInfo.id = TestInfoId;
-    mInfo.name = TestInfoName;
-    mInfo.flags = TestInfoFlags;
-    mInfo.type = TestInfoType;
-    mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
-    mInfo.frameLeft = TestInfoFrameLeft;
-    mInfo.frameTop = TestInfoFrameTop;
-    mInfo.frameRight = TestInfoFrameRight;
-    mInfo.frameBottom = TestInfoFrameBottom;
-    mInfo.surfaceInset = TestInfoSurfaceInset;
-    mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
-    mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
-                         TestInfoFrameTop, 0, 0, 1});
-    mInfo.touchableRegion = TestInfoTouchableRegion;
-    mInfo.visible = TestInfoVisible;
-    mInfo.trustedOverlay = TestInfoTrustedOverlay;
-    mInfo.focusable = TestInfoFocusable;
-
-    mInfo.hasWallpaper = TestInfoHasWallpaper;
-    mInfo.paused = TestInfoPaused;
-    mInfo.ownerPid = TestInfoOwnerPid;
-    mInfo.ownerUid = TestInfoOwnerUid;
-    mInfo.inputFeatures = TestInfoInputFeatures;
-    mInfo.displayId = TestInfoDisplayId;
-    mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
-    mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
-    mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
-
-    mInfo.applicationInfo.name = TestAppInfoName;
-    mInfo.applicationInfo.token = TestAppInfoToken;
-    mInfo.applicationInfo.dispatchingTimeoutMillis =
-            std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout)
-                    .count();
-
     InitializeInputFlinger();
 }
 
@@ -331,10 +174,6 @@
     mQuery->resetInputManager();
 }
 
-void InputFlingerServiceTest::verifyInputWindowInfo(const WindowInfo& info) const {
-    EXPECT_EQ(mInfo, info);
-}
-
 void InputFlingerServiceTest::InitializeInputFlinger() {
     sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
     ASSERT_TRUE(input != nullptr);
@@ -345,40 +184,6 @@
     mQuery = interface_cast<IInputFlingerQuery>(input);
 }
 
-void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<WindowInfo>& infos) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mService->setInputWindows(infos, mSetInputWindowsListener);
-    // Verify listener call
-    EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
-}
-
-void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
-                                               const sp<IBinder> focusedToken,
-                                               nsecs_t timestampNanos) {
-    FocusRequest request;
-    request.token = TestInfoToken;
-    request.focusedToken = focusedToken;
-    request.timestamp = timestampNanos;
-    mService->setFocusedWindow(request);
-    // call set input windows and wait for the callback to drain the queue.
-    setInputWindowsByInfos(std::vector<WindowInfo>());
-}
-
-/**
- *  Test InputFlinger service interface SetInputWindows
- */
-TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
-    std::vector<WindowInfo> infos = {getInfo()};
-    setInputWindowsByInfos(infos);
-
-    // Verify input windows from service
-    std::vector<WindowInfo> windowInfos;
-    mQuery->getInputWindows(&windowInfos);
-    for (const WindowInfo& windowInfo : windowInfos) {
-        verifyInputWindowInfo(windowInfo);
-    }
-}
-
 /**
  *  Test InputFlinger service interface createInputChannel
  */
@@ -397,7 +202,7 @@
     EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
 }
 
-TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+TEST_F(InputFlingerServiceTest, CreateInputChannel) {
     InputChannel channel;
     ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
 
@@ -411,30 +216,6 @@
     EXPECT_EQ(channels.size(), 0UL);
 }
 
-TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
-
-    FocusRequest request;
-    mQuery->getLastFocusRequest(&request);
-
-    EXPECT_EQ(request.token, TestInfoToken);
-    EXPECT_EQ(request.focusedToken, nullptr);
-    EXPECT_EQ(request.timestamp, now);
-}
-
-TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
-
-    FocusRequest request;
-    mQuery->getLastFocusRequest(&request);
-
-    EXPECT_EQ(request.token, TestInfoToken);
-    EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
-    EXPECT_EQ(request.timestamp, now);
-}
-
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 778f6e6..43f14bd 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,6 +16,7 @@
 
 #include <CursorInputMapper.h>
 #include <InputDevice.h>
+#include <InputFlingerProperties.sysprop.h>
 #include <InputMapper.h>
 #include <InputReader.h>
 #include <InputReaderBase.h>
@@ -95,6 +96,17 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
+static int32_t getInverseRotation(int32_t orientation) {
+    switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            return DISPLAY_ORIENTATION_270;
+        case DISPLAY_ORIENTATION_270:
+            return DISPLAY_ORIENTATION_90;
+        default:
+            return orientation;
+    }
+}
+
 // --- FakePointerController ---
 
 class FakePointerController : public PointerControllerInterface {
@@ -2660,6 +2672,7 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
+    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2676,11 +2689,19 @@
         mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
-    void SetUp() override { SetUp(DEVICE_CLASSES); }
+    void SetUp() override {
+        // Ensure per_window_input_rotation is enabled.
+        sysprop::InputFlingerProperties::per_window_input_rotation(true);
+
+        SetUp(DEVICE_CLASSES);
+    }
 
     void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
+
+        sysprop::InputFlingerProperties::per_window_input_rotation(
+                INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE);
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
@@ -2792,6 +2813,8 @@
 const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
         Flags<InputDeviceClass>(0); // not needed for current tests
 const int32_t InputMapperTest::EVENTHUB_ID = 1;
+const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
+        sysprop::InputFlingerProperties::per_window_input_rotation();
 
 // --- SwitchInputMapperTest ---
 
@@ -4088,8 +4111,11 @@
     ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
+TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
     addConfigurationProperty("cursor.mode", "navigation");
+    // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
+    // need to be rotated.
+    addConfigurationProperty("cursor.orientationAware", "1");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
@@ -4103,9 +4129,10 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 }
 
-TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
+TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
     addConfigurationProperty("cursor.mode", "navigation");
-    addConfigurationProperty("cursor.orientationAware", "1");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -4119,14 +4146,14 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1,  1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1,  1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1,  1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
     prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
@@ -4139,14 +4166,14 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1, -1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1, -1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
@@ -5368,11 +5395,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
-TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_DoesNotRotateMotions) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
-    addConfigurationProperty("touch.orientationAware", "0");
+    // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
+    // need to be rotated. Touchscreens are orientation-aware by default.
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
@@ -5391,10 +5419,13 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
-TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
+TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotions) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
@@ -5416,7 +5447,7 @@
     // Rotation 90.
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
+    processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
@@ -5444,7 +5475,7 @@
     // Rotation 270.
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
+    processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
@@ -7806,7 +7837,7 @@
     ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames);
 }
 
-TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
+TEST_F(MultiTouchInputMapperTest, VideoFrames_AreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7816,6 +7847,32 @@
 
     // Test all 4 orientations
     for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
+                                DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+        SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+        clearViewports();
+        prepareDisplay(orientation);
+        std::vector<TouchVideoFrame> frames{frame};
+        mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
+        processPosition(mapper, 100, 200);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+        ASSERT_EQ(frames, motionArgs.videoFrames);
+    }
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    // Unrotated video frame
+    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+    NotifyMotionArgs motionArgs;
+
+    // Test all 4 orientations
+    for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
              DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
         SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
         clearViewports();
@@ -7825,12 +7882,16 @@
         processPosition(mapper, 100, 200);
         processSync(mapper);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-        frames[0].rotate(orientation);
+        // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction
+        // compared to the display. This is so that when the window transform (which contains the
+        // display rotation) is applied later by InputDispatcher, the coordinates end up in the
+        // window's coordinate space.
+        frames[0].rotate(getInverseRotation(orientation));
         ASSERT_EQ(frames, motionArgs.videoFrames);
     }
 }
 
-TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
+TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7847,8 +7908,36 @@
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    std::for_each(frames.begin(), frames.end(),
-            [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); });
+    ASSERT_EQ(frames, motionArgs.videoFrames);
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_MultipleFramesAreRotated) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    // Unrotated video frames. There's no rule that they must all have the same dimensions,
+    // so mix these.
+    TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+    TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3});
+    TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4});
+    std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
+    NotifyMotionArgs motionArgs;
+
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    std::for_each(frames.begin(), frames.end(), [](TouchVideoFrame& frame) {
+        // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction
+        // compared to the display. This is so that when the window transform (which contains the
+        // display rotation) is applied later by InputDispatcher, the coordinates end up in the
+        // window's coordinate space.
+        frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+    });
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
@@ -8402,18 +8491,25 @@
     // Reset.
     mapper.reset(ARBITRARY_TIME);
 
-    // Let physical display be different to device, and make surface and physical could be 1:1.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0);
+    // Let physical display be different to device, and make surface and physical could be 1:1 in
+    // all four orientations.
+    for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+                            DISPLAY_ORIENTATION_270}) {
+        halfDisplayToCenterHorizontal(orientation);
 
-    const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
-    const int32_t yExpected = y;
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+        const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
+        const int32_t yExpected = y;
+        processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+    }
 }
 
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) {
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
@@ -8422,34 +8518,40 @@
     const int32_t x = DISPLAY_WIDTH / 4;
     const int32_t y = DISPLAY_HEIGHT / 2;
 
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected = y;
-    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
-    const int32_t x = DISPLAY_WIDTH / 4;
-    const int32_t y = DISPLAY_HEIGHT / 2;
-
     // expect x/y = swap x/y then reverse x.
     constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
     constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
     processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
 }
 
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) {
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected = y;
+    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     const int32_t x = 0;
@@ -8461,16 +8563,16 @@
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected90 = y;
-    const int32_t yExpected90 = DISPLAY_WIDTH - 1;
+    // expect x/y = swap x/y then reverse x.
+    const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
+    const int32_t yExpected90 = x;
     processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    // expect x/y = swap x/y then reverse x.
-    const int32_t xExpected270 = DISPLAY_HEIGHT - 1;
-    const int32_t yExpected270 = x;
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected270 = y;
+    const int32_t yExpected270 = DISPLAY_WIDTH - 1;
     processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
 }
 
diff --git a/services/memtrackproxy/Android.bp b/services/memtrackproxy/Android.bp
index 7d78f3b..3233cc9 100644
--- a/services/memtrackproxy/Android.bp
+++ b/services/memtrackproxy/Android.bp
@@ -32,7 +32,7 @@
         "libcutils",
         "libutils",
         "android.hardware.memtrack@1.0",
-        "android.hardware.memtrack-V1-ndk_platform",
+        "android.hardware.memtrack-V1-ndk",
     ],
     srcs: [
         "MemtrackProxy.cpp",
@@ -45,6 +45,6 @@
     ],
     export_shared_lib_headers: [
         "android.hardware.memtrack@1.0",
-        "android.hardware.memtrack-V1-ndk_platform",
+        "android.hardware.memtrack-V1-ndk",
     ],
 }
diff --git a/services/memtrackproxy/test/Android.bp b/services/memtrackproxy/test/Android.bp
index f943761..1dc21bf 100644
--- a/services/memtrackproxy/test/Android.bp
+++ b/services/memtrackproxy/test/Android.bp
@@ -29,7 +29,7 @@
     shared_libs: [
         "libbinder_ndk",
         "libmemtrackproxy",
-        "android.hardware.memtrack-V1-ndk_platform",
+        "android.hardware.memtrack-V1-ndk",
     ],
     test_suites: ["general-tests"],
     require_root: true,
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 5d6f8c7..c233bf0 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -915,7 +915,7 @@
     if (activationIndex < 0) {
         return false;
     }
-    return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
+    return mActivationCount.valueAt(activationIndex).isActive;
 }
 
 void SensorDevice::onMicSensorAccessChanged(void* ident, int handle, nsecs_t samplingPeriodNs) {
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 560834f..c285c00 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -92,14 +92,16 @@
 }
 
 status_t ProximitySensor::activate(void* ident, bool enabled) {
-    bool wasActive = mActive;
+    bool lastState = mSensorDevice.isSensorActive(mSensor.getHandle());
+
     status_t status = HardwareSensor::activate(ident, enabled);
     if (status != NO_ERROR) {
         return status;
     }
-    mActive = enabled;
-    if (wasActive != enabled) {
-        mSensorService.onProximityActiveLocked(enabled);
+
+    bool currentState = mSensorDevice.isSensorActive(mSensor.getHandle());
+    if (currentState != lastState) {
+        mSensorService.onProximityActiveLocked(currentState);
     }
     return NO_ERROR;
 }
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index ea181c9..4e9f7bf 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -119,7 +119,6 @@
     void didEnableAllSensors() override;
 private:
     SensorService& mSensorService;
-    bool mActive;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index a472c5f..7fea616 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -16,7 +16,7 @@
     cflags: ["-Wall", "-Werror"],
     shared_libs: [
         "android.frameworks.stats@1.0",
-        "android.frameworks.stats-V1-ndk_platform",
+        "android.frameworks.stats-V1-ndk",
         "libbinder_ndk",
         "libhidlbase",
         "liblog",
@@ -29,7 +29,7 @@
     ],
     export_shared_lib_headers: [
         "android.frameworks.stats@1.0",
-        "android.frameworks.stats-V1-ndk_platform",
+        "android.frameworks.stats-V1-ndk",
     ],
     local_include_dirs: [
         "include/stats",
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 63f7efc..eeb3f3a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -146,6 +146,7 @@
         "DisplayHardware/ComposerHal.cpp",
         "DisplayHardware/DisplayIdentification.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
+        "DisplayHardware/Hash.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
@@ -157,6 +158,7 @@
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "HdrLayerInfoReporter.cpp",
+        "WindowInfosListenerInvoker.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 2fa8936..81254ac 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -520,13 +520,10 @@
 }
 
 status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
-    uint32_t const maxSurfaceDims =
-          std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
-
     // never allow a surface larger than what our underlying GL implementation
     // can handle.
-    if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
-        ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
+    if (mFlinger->exceedsMaxRenderTargetSize(w, h)) {
+        ALOGE("dimensions too large %" PRIu32 " x %" PRIu32, w, h);
         return BAD_VALUE;
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 032ff9a..8bc51df 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -425,7 +425,8 @@
 
     if (mDrawingState.buffer) {
         mReleasePreviousBuffer = true;
-        if (mDrawingState.buffer != mBufferInfo.mBuffer) {
+        if (mDrawingState.buffer != mBufferInfo.mBuffer ||
+            mDrawingState.frameNumber != mBufferInfo.mFrameNumber) {
             // If mDrawingState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
             // dropped and we should decrement the pending buffer count and
@@ -890,7 +891,11 @@
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
     mBufferInfo.mTransform = s.bufferTransform;
+    auto lastDataspace = mBufferInfo.mDataspace;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
+    if (lastDataspace != mBufferInfo.mDataspace) {
+        mFlinger->mSomeDataspaceChanged = true;
+    }
     mBufferInfo.mCrop = computeBufferCrop(s);
     mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
@@ -959,22 +964,6 @@
     ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
 }
 
-void BufferStateLayer::bufferMayChange(const sp<GraphicBuffer>& newBuffer) {
-    if (mDrawingState.buffer != nullptr &&
-        (!mBufferInfo.mBuffer ||
-         mDrawingState.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) &&
-        newBuffer != mDrawingState.buffer->getBuffer()) {
-        // If we are about to update mDrawingState.buffer but it has not yet latched
-        // then we will drop a buffer and should decrement the pending buffer count and
-        // call any release buffer callbacks if set.
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                  mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                  mDrawingState.acquireFence, mTransformHint,
-                                  mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                          mOwnerUid));
-        decrementPendingBufferCount();
-    }
-}
 
 /*
  * We don't want to send the layer's transform to input, but rather the
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index e567478..cab4899 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -97,7 +97,6 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    void bufferMayChange(const sp<GraphicBuffer>& newBuffer) override;
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index aac6c91..8da2e24 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -132,6 +128,3 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index e2b07f0..15a86af 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -166,6 +166,9 @@
     // Enables (or disables) layer caching on this output
     virtual void setLayerCachingEnabled(bool) = 0;
 
+    // Enables (or disables) layer caching texture pool on this output
+    virtual void setLayerCachingTexturePoolEnabled(bool) = 0;
+
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 2e40487..14f2163 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -41,6 +41,7 @@
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
+    void setLayerCachingTexturePoolEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySize(const ui::Size&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 7534548..8ec15ed 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -80,6 +80,8 @@
     void renderCachedSets(const OutputCompositionState& outputState,
                           std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
+    void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); }
+
     void dump(std::string& result) const;
     void dumpLayers(std::string& result) const;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index be34153..76d5e81 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -64,6 +64,8 @@
     void renderCachedSets(const OutputCompositionState& outputState,
                           std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
+    void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); }
+
     void dump(const Vector<String16>& args, std::string&);
 
 private:
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index fb53ee0..d607c75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -63,7 +63,8 @@
         sp<Fence> mFence;
     };
 
-    TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {}
+    TexturePool(renderengine::RenderEngine& renderEngine)
+          : mRenderEngine(renderEngine), mEnabled(false) {}
 
     virtual ~TexturePool() = default;
 
@@ -78,6 +79,12 @@
     // to the pool.
     std::shared_ptr<AutoTexture> borrowTexture();
 
+    // Enables or disables the pool. When the pool is disabled, no buffers will
+    // be held by the pool. This is useful when the active display changes.
+    void setEnabled(bool enable);
+
+    void dump(std::string& out) const;
+
 protected:
     // Proteted visibility so that they can be used for testing
     const static constexpr size_t kMinPoolSize = 3;
@@ -95,8 +102,10 @@
     // Returns a previously borrowed texture to the pool.
     void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
                        const sp<Fence>& fence);
+    void allocatePool();
     renderengine::RenderEngine& mRenderEngine;
     ui::Size mSize;
+    bool mEnabled;
 };
 
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 60ff4b1..216019f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -37,6 +37,7 @@
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
+    MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3c3c7f3..6ac488b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -146,6 +146,12 @@
     }
 }
 
+void Output::setLayerCachingTexturePoolEnabled(bool enabled) {
+    if (mPlanner) {
+        mPlanner->setTexturePoolEnabled(enabled);
+    }
+}
+
 void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index b82bdbc..ccacdfb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -174,7 +174,7 @@
     LayerFE::ClientCompositionTargetSettings targetSettings{
             .clip = Region(viewport),
             .needsFiltering = false,
-            .isSecure = true,
+            .isSecure = outputState.isSecure,
             .supportsProtectedContent = false,
             .viewport = viewport,
             .dataspace = outputDataspace,
@@ -216,6 +216,7 @@
     }
 
     renderengine::LayerSettings holePunchSettings;
+    renderengine::LayerSettings holePunchBackgroundSettings;
     if (mHolePunchLayer) {
         auto clientCompositionList =
                 mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
@@ -230,6 +231,15 @@
         holePunchSettings.alpha = 0.0f;
         holePunchSettings.name = std::string("hole punch layer");
         layerSettingsPointers.push_back(&holePunchSettings);
+
+        // Add a solid background as the first layer in case there is no opaque
+        // buffer behind the punch hole
+        holePunchBackgroundSettings.alpha = 1.0f;
+        holePunchBackgroundSettings.name = std::string("holePunchBackground");
+        holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
+        holePunchBackgroundSettings.geometry.positionTransform =
+                holePunchSettings.geometry.positionTransform;
+        layerSettingsPointers.insert(layerSettingsPointers.begin(), &holePunchBackgroundSettings);
     }
 
     if (sDebugHighlighLayers) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index f033279..8e2c182 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -205,6 +205,9 @@
                         durationString(lastUpdate).c_str());
 
     dumpLayers(result);
+
+    base::StringAppendF(&result, "\n");
+    mTexturePool.dump(result);
 }
 
 size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index e3772a2..497c433 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -24,14 +24,22 @@
 
 namespace android::compositionengine::impl::planner {
 
+void TexturePool::allocatePool() {
+    mPool.clear();
+    if (mEnabled && mSize.isValid()) {
+        mPool.resize(kMinPoolSize);
+        std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
+            return Entry{genTexture(), nullptr};
+        });
+    }
+}
+
 void TexturePool::setDisplaySize(ui::Size size) {
     if (mSize == size) {
         return;
     }
     mSize = size;
-    mPool.clear();
-    mPool.resize(kMinPoolSize);
-    std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; });
+    allocatePool();
 }
 
 std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
@@ -46,7 +54,12 @@
 
 void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
                                 const sp<Fence>& fence) {
-    // Drop the texture on the floor if the pool is no longer tracking textures of the same size.
+    // Drop the texture on the floor if the pool is not enabled
+    if (!mEnabled) {
+        return;
+    }
+
+    // Or the texture on the floor if the pool is no longer tracking textures of the same size.
     if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
         static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
         ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), "
@@ -81,4 +94,15 @@
                                                    renderengine::ExternalTexture::Usage::WRITEABLE);
 }
 
+void TexturePool::setEnabled(bool enabled) {
+    mEnabled = enabled;
+    allocatePool();
+}
+
+void TexturePool::dump(std::string& out) const {
+    base::StringAppendF(&out,
+                        "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
+                        mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
+}
+
 } // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 65c97b7..224dad1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -47,9 +47,7 @@
 
     MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
     MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
-    MOCK_METHOD4(allocateVirtualDisplay,
-                 bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                      std::optional<PhysicalDisplayId>));
+    MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
 
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
@@ -113,11 +111,15 @@
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
-    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
-    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
+
+    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+    MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+                (const, override));
+    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+                (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index b05a594..ec81322 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -51,6 +51,15 @@
 
     return expectedBlurSetting == arg.blurSetting;
 }
+
+MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") {
+    *result_listener << "ClientCompositionTargetSettings' SecureSettings aren't equal \n";
+    *result_listener << "expected " << expectedSecureSetting << "\n";
+    *result_listener << "actual " << arg.isSecure << "\n";
+
+    return expectedSecureSetting == arg.isSecure;
+}
+
 static const ui::Size kOutputSize = ui::Size(1, 1);
 
 class CachedSetTest : public testing::Test {
@@ -315,7 +324,7 @@
     EXPECT_EQ(0u, cachedSet.getAge());
 }
 
-TEST_F(CachedSetTest, render) {
+TEST_F(CachedSetTest, renderUnsecureOutput) {
     // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
     CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
     sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
@@ -348,9 +357,66 @@
         return NO_ERROR;
     };
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1,
+                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    mOutputState.isSecure = false;
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    expectReadyBuffer(cachedSet);
+
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+    EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, renderSecureOutput) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    clientCompList1[0].alpha = 0.5f;
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    clientCompList2[0].alpha = 0.75f;
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
+        EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
+        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+                  displaySettings.orientation);
+        EXPECT_EQ(0.5f, layers[0]->alpha);
+        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(*layerFE1,
+                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
 
@@ -590,12 +656,22 @@
                                 base::unique_fd&&, base::unique_fd*) -> size_t {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
-        EXPECT_GE(layers.size(), 3u);
-        const auto* holePunchSettings = layers[2];
-        EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
-        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
-        EXPECT_TRUE(holePunchSettings->disableBlending);
-        EXPECT_EQ(0.0f, holePunchSettings->alpha);
+        EXPECT_GE(layers.size(), 4u);
+        {
+            const auto* holePunchSettings = layers[3];
+            EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+            EXPECT_TRUE(holePunchSettings->disableBlending);
+            EXPECT_EQ(0.0f, holePunchSettings->alpha);
+        }
+
+        {
+            const auto* holePunchBackgroundSettings = layers[0];
+            EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
+            EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
+            EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+        }
 
         return NO_ERROR;
     };
@@ -640,12 +716,23 @@
                                 base::unique_fd&&, base::unique_fd*) -> size_t {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
-        EXPECT_GE(layers.size(), 3u);
-        const auto* holePunchSettings = layers[2];
-        EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
-        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
-        EXPECT_TRUE(holePunchSettings->disableBlending);
-        EXPECT_EQ(0.0f, holePunchSettings->alpha);
+        EXPECT_GE(layers.size(), 4u);
+
+        {
+            const auto* holePunchSettings = layers[3];
+            EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+            EXPECT_TRUE(holePunchSettings->disableBlending);
+            EXPECT_EQ(0.0f, holePunchSettings->alpha);
+        }
+
+        {
+            const auto* holePunchBackgroundSettings = layers[0];
+            EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
+            EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
+            EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
+            EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+        }
 
         return NO_ERROR;
     };
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index b802e51..6fc90fe 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -42,6 +42,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        mTexturePool.setEnabled(true);
         mTexturePool.setDisplaySize(kDisplaySize);
     }
 
@@ -130,5 +131,44 @@
               static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
 }
 
+TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+
+    std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
+    for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+        textures.emplace_back(mTexturePool.borrowTexture());
+    }
+
+    EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+
+    textures.clear();
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+}
+
+TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+
+    std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
+    for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+        textures.emplace_back(mTexturePool.borrowTexture());
+    }
+
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+    textures.clear();
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+}
+
+TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+    mTexturePool.setEnabled(true);
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+}
+
 } // namespace
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index afd12b5..7db87d9 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -273,7 +273,7 @@
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
     uint32_t mFlags = 0;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index caf0294..09734c2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -212,7 +212,7 @@
 }
 
 Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                                     std::optional<Display>, Display* outDisplay) {
+                                     Display* outDisplay) {
     const uint32_t bufferSlotCount = 1;
     Error error = kDefaultError;
     if (mClient_2_2) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index b525e63..fe114b9 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -96,7 +96,7 @@
 
     virtual uint32_t getMaxVirtualDisplayCount() = 0;
     virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*,
-                                       std::optional<Display> mirror, Display* outDisplay) = 0;
+                                       Display* outDisplay) = 0;
     virtual Error destroyVirtualDisplay(Display display) = 0;
 
     virtual Error acceptDisplayChanges(Display display) = 0;
@@ -342,7 +342,7 @@
 
     uint32_t getMaxVirtualDisplayCount() override;
     Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                               std::optional<Display> mirror, Display* outDisplay) override;
+                               Display* outDisplay) override;
     Error destroyVirtualDisplay(Display display) override;
 
     Error acceptDisplayChanges(Display display) override;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 98209bb..83c2b2e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -25,6 +25,7 @@
 #include <log/log.h>
 
 #include "DisplayIdentification.h"
+#include "Hash.h"
 
 namespace android {
 namespace {
@@ -262,8 +263,9 @@
     }
 
     // Hash model string instead of using product code or (integer) serial number, since the latter
-    // have been observed to change on some displays with multiple inputs.
-    const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+    // have been observed to change on some displays with multiple inputs. Use a stable hash instead
+    // of std::hash which is only required to be same within a single execution of a program.
+    const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString));
 
     // Parse extension blocks.
     std::optional<Cea861ExtensionBlock> cea861Block;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7e45dab..256bca9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -212,8 +212,6 @@
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
     auto& displayData = mDisplayData[*displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(*displayId).c_str());
 
     {
         // There have been reports of HWCs that signal several vsync events
@@ -245,8 +243,7 @@
 }
 
 bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size resolution,
-                                        ui::PixelFormat* format,
-                                        std::optional<PhysicalDisplayId> mirror) {
+                                        ui::PixelFormat* format) {
     if (!resolution.isValid()) {
         ALOGE("%s: Invalid resolution %dx%d", __func__, resolution.width, resolution.height);
         return false;
@@ -262,14 +259,9 @@
         return false;
     }
 
-    std::optional<hal::HWDisplayId> hwcMirrorId;
-    if (mirror) {
-        hwcMirrorId = fromPhysicalDisplayId(*mirror);
-    }
-
     hal::HWDisplayId hwcDisplayId;
     const auto error = static_cast<hal::Error>(
-            mComposer->createVirtualDisplay(width, height, format, hwcMirrorId, &hwcDisplayId));
+            mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     RETURN_IF_HWC_ERROR_FOR("createVirtualDisplay", error, displayId, false);
 
     auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
@@ -277,7 +269,6 @@
     display->setConnected(true);
     auto& displayData = mDisplayData[displayId];
     displayData.hwcDisplay = std::move(display);
-    displayData.isVirtual = true;
     return true;
 }
 
@@ -285,10 +276,8 @@
                                          PhysicalDisplayId displayId) {
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 
-    if (!mInternalHwcDisplayId) {
-        mInternalHwcDisplayId = hwcDisplayId;
-    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
-        mExternalHwcDisplayId = hwcDisplayId;
+    if (!mPrimaryHwcDisplayId) {
+        mPrimaryHwcDisplayId = hwcDisplayId;
     }
 
     auto& displayData = mDisplayData[displayId];
@@ -378,7 +367,7 @@
     ui::DisplayConnectionType type;
     const auto error = hwcDisplay->getConnectionType(&type);
 
-    const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+    const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId
             ? ui::DisplayConnectionType::Internal
             : ui::DisplayConnectionType::External;
 
@@ -434,9 +423,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
     // that even if HWC blocks (which it shouldn't), it won't
@@ -603,14 +589,11 @@
 status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    const auto& displayData = mDisplayData[displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     if (mode == hal::PowerMode::OFF) {
         setVsyncEnabled(displayId, hal::Vsync::DISABLE);
     }
 
+    const auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
     switch (mode) {
         case hal::PowerMode::OFF:
@@ -684,12 +667,6 @@
     auto& displayData = mDisplayData[displayId];
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
 
-    // TODO(b/74619554): Select internal/external display from remaining displays.
-    if (hwcDisplayId == mInternalHwcDisplayId) {
-        mInternalHwcDisplayId.reset();
-    } else if (hwcDisplayId == mExternalHwcDisplayId) {
-        mExternalHwcDisplayId.reset();
-    }
     mPhysicalDisplayIdMap.erase(hwcDisplayId);
     mDisplayData.erase(displayId);
 }
@@ -699,9 +676,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
@@ -859,8 +833,7 @@
 
 std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
         PhysicalDisplayId displayId) const {
-    if (const auto it = mDisplayData.find(displayId);
-        it != mDisplayData.end() && !it->second.isVirtual) {
+    if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) {
         return it->second.hwcDisplay->getId();
     }
     return {};
@@ -874,7 +847,8 @@
         return true;
     }
 
-    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
+    // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL.
+    if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) {
         ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
         return true;
     }
@@ -915,7 +889,7 @@
         }
 
         info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
-            const bool isPrimary = !mInternalHwcDisplayId;
+            const bool isPrimary = !mPrimaryHwcDisplayId;
             if (mHasMultiDisplaySupport) {
                 if (const auto info = parseDisplayIdentificationData(port, data)) {
                     return *info;
@@ -928,8 +902,8 @@
             }
 
             return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
-                                             .name = isPrimary ? "Internal display"
-                                                               : "External display",
+                                             .name = isPrimary ? "Primary display"
+                                                               : "Secondary display",
                                              .deviceProductInfo = std::nullopt};
         }();
     }
@@ -942,9 +916,12 @@
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
         hal::HWDisplayId hwcDisplayId) {
+    LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId,
+                        "Primary display cannot be disconnected.");
+
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
-        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
         return {};
     }
 
@@ -953,7 +930,7 @@
     if (isConnected(*displayId)) {
         mDisplayData[*displayId].hwcDisplay->setConnected(false);
     } else {
-        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
     }
     // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
     // via SurfaceFlinger's onHotplugReceived callback handling
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b1849e8..0a090da 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
+#pragma once
 
 #include <cstdint>
 #include <future>
@@ -113,11 +112,7 @@
 
     // Attempts to allocate a virtual display on the HWC. The maximum number of virtual displays
     // supported by the HWC can be queried in advance, but allocation may fail for other reasons.
-    // For virtualized compositors, the PhysicalDisplayId is a hint that this virtual display is
-    // a mirror of a physical display, and that the screen should be captured by the host rather
-    // than guest compositor.
-    virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                                        std::optional<PhysicalDisplayId> mirror) = 0;
+    virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
 
     virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
 
@@ -232,14 +227,18 @@
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
-    // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
     virtual Hwc2::Composer* getComposer() const = 0;
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
-    virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
+    // Returns the first display connected at boot. It cannot be disconnected, which implies an
+    // internal connection type. Its connection via HWComposer::onHotplug, which in practice is
+    // immediately after HWComposer construction, must occur before any call to this function.
+    //
+    // TODO(b/182939859): Remove special cases for primary display.
+    virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0;
+    virtual PhysicalDisplayId getPrimaryDisplayId() const = 0;
+    virtual bool isHeadless() const = 0;
 
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
@@ -265,8 +264,7 @@
     size_t getMaxVirtualDisplayCount() const override;
     size_t getMaxVirtualDisplayDimension() const override;
 
-    bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                                std::optional<PhysicalDisplayId>) override;
+    bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
 
     // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
     void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
@@ -371,14 +369,19 @@
 
     Hwc2::Composer* getComposer() const override { return mComposer.get(); }
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override {
-        return mInternalHwcDisplayId;
+    hal::HWDisplayId getPrimaryHwcDisplayId() const override {
+        LOG_ALWAYS_FATAL_IF(!mPrimaryHwcDisplayId, "Missing HWC primary display");
+        return *mPrimaryHwcDisplayId;
     }
-    std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override {
-        return mExternalHwcDisplayId;
+
+    PhysicalDisplayId getPrimaryDisplayId() const override {
+        const auto id = toPhysicalDisplayId(getPrimaryHwcDisplayId());
+        LOG_ALWAYS_FATAL_IF(!id, "Missing primary display");
+        return *id;
     }
 
+    virtual bool isHeadless() const override { return !mPrimaryHwcDisplayId; }
+
     std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
     std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
 
@@ -387,12 +390,9 @@
     friend TestableSurfaceFlinger;
 
     struct DisplayData {
-        bool isVirtual = false;
         std::unique_ptr<HWC2::Display> hwcDisplay;
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
-        buffer_handle_t outbufHandle = nullptr;
-        sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
 
         bool validateWasSkipped;
         hal::Error presentError;
@@ -423,8 +423,7 @@
     bool mRegisteredCallback = false;
 
     std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
-    std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
-    std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
+    std::optional<hal::HWDisplayId> mPrimaryHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
     const size_t mMaxVirtualDisplayDimension;
@@ -433,5 +432,3 @@
 
 } // namespace impl
 } // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/Hash.cpp b/services/surfaceflinger/DisplayHardware/Hash.cpp
new file mode 100644
index 0000000..6056c8d
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/Hash.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DisplayIdentification"
+
+#include <cstring>
+#include <type_traits>
+
+#include <log/log.h>
+
+#include "Hash.h"
+
+namespace android {
+namespace {
+
+template <class T>
+inline T load(const void* p) {
+    static_assert(std::is_integral<T>::value, "T must be integral");
+
+    T r;
+    std::memcpy(&r, p, sizeof(r));
+    return r;
+}
+
+uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
+    return (val >> shift) | (val << (64 - shift));
+}
+
+uint64_t shiftMix(uint64_t val) {
+    return val ^ (val >> 47);
+}
+
+uint64_t hash64Len16(uint64_t u, uint64_t v) {
+    constexpr uint64_t kMul = 0x9ddfea08eb382d69;
+    uint64_t a = (u ^ v) * kMul;
+    a ^= (a >> 47);
+    uint64_t b = (v ^ a) * kMul;
+    b ^= (b >> 47);
+    b *= kMul;
+    return b;
+}
+
+uint64_t hash64Len0To16(const char* s, uint64_t len) {
+    constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
+    constexpr uint64_t k3 = 0xc949d7c7509e6557;
+
+    if (len > 8) {
+        const uint64_t a = load<uint64_t>(s);
+        const uint64_t b = load<uint64_t>(s + len - 8);
+        return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
+    }
+    if (len >= 4) {
+        const uint32_t a = load<uint32_t>(s);
+        const uint32_t b = load<uint32_t>(s + len - 4);
+        return hash64Len16(len + (a << 3), b);
+    }
+    if (len > 0) {
+        const unsigned char a = static_cast<unsigned char>(s[0]);
+        const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
+        const unsigned char c = static_cast<unsigned char>(s[len - 1]);
+        const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+        const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
+        return shiftMix(y * k2 ^ z * k3) * k2;
+    }
+    return k2;
+}
+
+} // namespace
+
+uint64_t cityHash64Len0To16(std::string_view sv) {
+    auto len = sv.length();
+    if (len > 16) {
+        ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
+        len = 16;
+    }
+    return hash64Len0To16(sv.data(), len);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/Hash.h b/services/surfaceflinger/DisplayHardware/Hash.h
new file mode 100644
index 0000000..a7b6c71
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/Hash.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string_view>
+
+namespace android {
+
+// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
+uint64_t cityHash64Len0To16(std::string_view sv);
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 93c86d1..87b0dbc 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -819,11 +819,7 @@
 }
 
 bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
-    sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
-    if (handle == nullptr) {
-        return false;
-    }
-    sp<Layer> relative = handle->owner.promote();
+    sp<Layer> relative = fromHandle(relativeToHandle).promote();
     if (relative == nullptr) {
         return false;
     }
@@ -851,6 +847,23 @@
     return true;
 }
 
+bool Layer::setTrustedOverlay(bool isTrustedOverlay) {
+    if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false;
+    mDrawingState.isTrustedOverlay = isTrustedOverlay;
+    mDrawingState.modified = true;
+    mFlinger->mInputInfoChanged = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::isTrustedOverlay() const {
+    if (getDrawingState().isTrustedOverlay) {
+        return true;
+    }
+    const auto& p = mDrawingParent.promote();
+    return (p != nullptr) && p->isTrustedOverlay();
+}
+
 bool Layer::setSize(uint32_t w, uint32_t h) {
     if (mDrawingState.requested_legacy.w == w && mDrawingState.requested_legacy.h == h)
         return false;
@@ -958,7 +971,6 @@
 }
 
 bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
-    mDrawingState.sequence++;
     mDrawingState.blurRegions = blurRegions;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1101,46 +1113,59 @@
     return StretchEffect{};
 }
 
+bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
+    // The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
+    const auto frameRate = [&] {
+        if (mDrawingState.frameRate.rate.isValid() ||
+            mDrawingState.frameRate.type == FrameRateCompatibility::NoVote) {
+            return mDrawingState.frameRate;
+        }
+
+        return parentFrameRate;
+    }();
+
+    *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+
+    // The frame rate is propagated to the children
+    bool childrenHaveFrameRate = false;
+    for (const sp<Layer>& child : mCurrentChildren) {
+        childrenHaveFrameRate |=
+                child->propagateFrameRateForLayerTree(frameRate, transactionNeeded);
+    }
+
+    // If we don't have a valid frame rate, but the children do, we set this
+    // layer as NoVote to allow the children to control the refresh rate
+    if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
+        childrenHaveFrameRate) {
+        *transactionNeeded |=
+                setFrameRateForLayerTree(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
+    }
+
+    // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
+    // the same reason we are allowing touch boost for those layers. See
+    // RefreshRateConfigs::getBestRefreshRate for more details.
+    const auto layerVotedWithDefaultCompatibility =
+            frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
+    const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
+    const auto layerVotedWithExactCompatibility =
+            frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Exact;
+    return layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+            layerVotedWithExactCompatibility || childrenHaveFrameRate;
+}
+
 void Layer::updateTreeHasFrameRateVote() {
-    const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
-        auto parent = getParent();
-        while (parent) {
-            visitor(parent.get());
-            parent = parent->getParent();
+    const auto root = [&]() -> sp<Layer> {
+        sp<Layer> layer = this;
+        while (auto parent = layer->getParent()) {
+            layer = parent;
         }
+        return layer;
+    }();
 
-        traverse(LayerVector::StateSet::Current, visitor);
-    };
-
-    // update parents and children about the vote
-    // First traverse the tree and count how many layers has votes.
-    int layersWithVote = 0;
-    traverseTree([&layersWithVote](Layer* layer) {
-        const auto layerVotedWithDefaultCompatibility =
-                layer->mDrawingState.frameRate.rate.isValid() &&
-                layer->mDrawingState.frameRate.type == FrameRateCompatibility::Default;
-        const auto layerVotedWithNoVote =
-                layer->mDrawingState.frameRate.type == FrameRateCompatibility::NoVote;
-        const auto layerVotedWithExactCompatibility =
-                layer->mDrawingState.frameRate.type == FrameRateCompatibility::Exact;
-
-        // We do not count layers that are ExactOrMultiple for the same reason
-        // we are allowing touch boost for those layers. See
-        // RefreshRateConfigs::getBestRefreshRate for more details.
-        if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
-            layerVotedWithExactCompatibility) {
-            layersWithVote++;
-        }
-    });
-
-    // Now we can update the tree frame rate vote for each layer in the tree
-    const bool treeHasFrameRateVote = layersWithVote > 0;
     bool transactionNeeded = false;
+    root->propagateFrameRateForLayerTree({}, &transactionNeeded);
 
-    traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) {
-        transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote);
-    });
-
+    // TODO(b/195668952): we probably don't need eTraversalNeeded here
     if (transactionNeeded) {
         mFlinger->setTransactionFlags(eTraversalNeeded);
     }
@@ -1267,42 +1292,23 @@
     return surfaceFrame;
 }
 
-bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) {
-    const auto updateDrawingState = [&](FrameRate frameRate) {
-        if (mDrawingState.frameRateForLayerTree == frameRate) {
-            return false;
-        }
-
-        mDrawingState.frameRateForLayerTree = frameRate;
-        mDrawingState.sequence++;
-        mDrawingState.modified = true;
-        setTransactionFlags(eTransactionNeeded);
-
-        mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
-                                                 LayerHistory::LayerUpdateType::SetFrameRate);
-
-        return true;
-    };
-
-    const auto frameRate = mDrawingState.frameRate;
-    if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
-        return updateDrawingState(frameRate);
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+    if (mDrawingState.frameRateForLayerTree == frameRate) {
+        return false;
     }
 
-    // This layer doesn't have a frame rate. Check if its ancestors have a vote
-    for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) {
-        if (parent->mDrawingState.frameRate.rate.isValid()) {
-            return updateDrawingState(parent->mDrawingState.frameRate);
-        }
-    }
+    mDrawingState.frameRateForLayerTree = frameRate;
 
-    // This layer and its ancestors don't have a frame rate. If one of successors
-    // has a vote, return a NoVote for successors to set the vote
-    if (treeHasFrameRateVote) {
-        return updateDrawingState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
-    }
+    // TODO(b/195668952): we probably don't need to dirty visible regions here
+    // or even store frameRateForLayerTree in mDrawingState
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
 
-    return updateDrawingState(frameRate);
+    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                             LayerHistory::LayerUpdateType::SetFrameRate);
+
+    return true;
 }
 
 Layer::FrameRate Layer::getFrameRateForLayerTree() const {
@@ -1599,8 +1605,7 @@
 bool Layer::reparent(const sp<IBinder>& newParentHandle) {
     sp<Layer> newParent;
     if (newParentHandle != nullptr) {
-        auto handle = static_cast<Handle*>(newParentHandle.get());
-        newParent = handle->owner.promote();
+        newParent = fromHandle(newParentHandle).promote();
         if (newParent == nullptr) {
             ALOGE("Unable to promote Layer handle");
             return false;
@@ -1975,24 +1980,10 @@
     mDrawingParent = mCurrentParent;
 }
 
-static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
-    if (weakBinderHandle == nullptr) {
-        return nullptr;
-    }
-    sp<IBinder> binderHandle = weakBinderHandle.promote();
-    if (binderHandle == nullptr) {
-        return nullptr;
-    }
-    sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
-    if (handle == nullptr) {
-        return nullptr;
-    }
-    return handle->owner;
-}
 
 void Layer::setInputInfo(const WindowInfo& info) {
     mDrawingState.inputInfo = info;
-    mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
+    mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
     mDrawingState.modified = true;
     mFlinger->mInputInfoChanged = true;
     setTransactionFlags(eTransactionNeeded);
@@ -2042,6 +2033,7 @@
 
         layerInfo->set_corner_radius(getRoundedCornerState().radius);
         layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
+        layerInfo->set_is_trusted_overlay(isTrustedOverlay());
         LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
@@ -2332,6 +2324,11 @@
                 toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
     }
 
+    // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
+    // if it was set by WM for a known system overlay
+    info.trustedOverlay = info.trustedOverlay || isTrustedOverlay();
+
+
     // If the layer is a clone, we need to crop the input region to cloned root to prevent
     // touches from going outside the cloned area.
     if (isClone()) {
@@ -2549,6 +2546,23 @@
     mFlinger->mNumClones++;
 }
 
+const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
+
+wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
+    if (handleBinder == nullptr) {
+        return nullptr;
+    }
+
+    BBinder* b = handleBinder->localBinder();
+    if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
+        return nullptr;
+    }
+
+    // We can safely cast this binder since its local and we verified its interface descriptor.
+    sp<Handle> handle = static_cast<Handle*>(handleBinder.get());
+    return handle->owner;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3aaa680..41bdebd 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -275,6 +275,9 @@
         // Stretch effect to apply to this layer
         StretchEffect stretchEffect;
 
+        // Whether or not this layer is a trusted overlay for input
+        bool isTrustedOverlay;
+
         Rect bufferCrop;
         Rect destinationFrame;
     };
@@ -286,16 +289,17 @@
     class LayerCleaner {
         sp<SurfaceFlinger> mFlinger;
         sp<Layer> mLayer;
+        BBinder* mHandle;
 
     protected:
         ~LayerCleaner() {
             // destroy client resources
-            mFlinger->onHandleDestroyed(mLayer);
+            mFlinger->onHandleDestroyed(mHandle, mLayer);
         }
 
     public:
-        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : mFlinger(flinger), mLayer(layer) {}
+        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
+              : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
     };
 
     /*
@@ -309,11 +313,15 @@
     class Handle : public BBinder, public LayerCleaner {
     public:
         Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : LayerCleaner(flinger, layer), owner(layer) {}
+              : LayerCleaner(flinger, layer, this), owner(layer) {}
+        const String16& getInterfaceDescriptor() const override { return kDescriptor; }
 
+        static const String16 kDescriptor;
         wp<Layer> owner;
     };
 
+    static wp<Layer> fromHandle(const sp<IBinder>& handle);
+
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
@@ -393,6 +401,7 @@
     virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
     virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
     virtual bool setTransparentRegionHint(const Region& transparent);
+    virtual bool setTrustedOverlay(bool);
     virtual bool setFlags(uint32_t flags, uint32_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
     virtual uint32_t getLayerStack() const;
@@ -699,12 +708,6 @@
     virtual uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
-     * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
-     * unlatched buffers in the drawing state.
-     */
-    virtual void bufferMayChange(const sp<GraphicBuffer>& /* newBuffer */){};
-
-    /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
      */
@@ -1049,7 +1052,10 @@
                                           const std::vector<Layer*>& layersInTree);
 
     void updateTreeHasFrameRateVote();
+    bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
+    bool setFrameRateForLayerTree(FrameRate);
     void setZOrderRelativeOf(const wp<Layer>& relativeOf);
+    bool isTrustedOverlay() const;
 
     // Find the root of the cloned hierarchy, this means the first non cloned parent.
     // This will return null if first non cloned parent is not found.
@@ -1066,8 +1072,6 @@
     // Fills in the frame and transform info for the gui::WindowInfo
     void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toPhysicalDisplay);
 
-    bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
-
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
     // a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 9a6c853..5fea521 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include "RenderArea.h"
 
 namespace android {
@@ -33,6 +29,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 28be962..b805bf6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -84,10 +84,13 @@
                                                       VSyncTracker& tracker, nsecs_t now) {
     auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
             std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+    auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
 
     bool const wouldSkipAVsyncTarget =
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
-    if (wouldSkipAVsyncTarget) {
+    bool const wouldSkipAWakeup =
+            mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
+    if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
         return getExpectedCallbackTime(nextVsyncTime, timing);
     }
 
@@ -97,9 +100,9 @@
     if (alreadyDispatchedForVsync) {
         nextVsyncTime =
                 tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+        nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
     }
 
-    auto const nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 194d808..245db0f 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -46,27 +46,36 @@
     return updateVsyncConfigLocked();
 }
 
-VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
-        TransactionSchedule schedule) {
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(TransactionSchedule schedule,
+                                                                      const sp<IBinder>& token) {
+    std::lock_guard<std::mutex> lock(mMutex);
     switch (schedule) {
         case Schedule::EarlyStart:
-            ALOGW_IF(mEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
-            mEarlyWakeup = true;
+            if (token) {
+                mEarlyWakeupRequests.emplace(token);
+                token->linkToDeath(this);
+            } else {
+                ALOGW("%s: EarlyStart requested without a valid token", __func__);
+            }
             break;
-        case Schedule::EarlyEnd:
-            ALOGW_IF(!mEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
-            mEarlyWakeup = false;
+        case Schedule::EarlyEnd: {
+            if (token && mEarlyWakeupRequests.erase(token) > 0) {
+                token->unlinkToDeath(this);
+            } else {
+                ALOGW("%s: Unexpected EarlyEnd", __func__);
+            }
             break;
+        }
         case Schedule::Late:
             // No change to mEarlyWakeup for non-explicit states.
             break;
     }
 
     if (mTraceDetailedInfo) {
-        ATRACE_INT("mEarlyWakeup", mEarlyWakeup);
+        ATRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size()));
     }
 
-    if (!mEarlyWakeup && schedule == Schedule::EarlyEnd) {
+    if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) {
         mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
         mEarlyTransactionStartTime = mNow();
     }
@@ -76,7 +85,7 @@
         return std::nullopt;
     }
     mTransactionSchedule = schedule;
-    return updateVsyncConfig();
+    return updateVsyncConfigLocked();
 }
 
 VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
@@ -128,8 +137,8 @@
 const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
-    if (mEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd || mEarlyTransactionFrames > 0 ||
-        mRefreshRateChangePending) {
+    if (!mEarlyWakeupRequests.empty() || mTransactionSchedule == Schedule::EarlyEnd ||
+        mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
         return mVsyncConfigSet.early;
     } else if (mEarlyGpuFrames > 0) {
         return mVsyncConfigSet.earlyGpu;
@@ -160,4 +169,11 @@
     return offsets;
 }
 
+void VsyncModulator::binderDied(const wp<IBinder>& who) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mEarlyWakeupRequests.erase(who);
+
+    static_cast<void>(updateVsyncConfigLocked());
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 9410768..b2b0451 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -19,8 +19,10 @@
 #include <chrono>
 #include <mutex>
 #include <optional>
+#include <unordered_set>
 
 #include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
 #include <utils/Timers.h>
 
 namespace android::scheduler {
@@ -35,7 +37,7 @@
 };
 
 // Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
-class VsyncModulator {
+class VsyncModulator : public IBinder::DeathRecipient {
 public:
     // Number of frames to keep early offsets after an early transaction or GPU composition.
     // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
@@ -91,7 +93,8 @@
     [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
 
     // Changes offsets in response to transaction flags or commit.
-    [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+    [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule,
+                                                        const sp<IBinder>& = {}) EXCLUDES(mMutex);
     [[nodiscard]] VsyncConfigOpt onTransactionCommit();
 
     // Called when we send a refresh rate change to hardware composer, so that
@@ -104,6 +107,10 @@
 
     [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
 
+protected:
+    // Called from unit tests as well
+    void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
+
 private:
     const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
     [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
@@ -116,8 +123,14 @@
 
     using Schedule = TransactionSchedule;
     std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
-    std::atomic<bool> mEarlyWakeup = false;
 
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
     std::atomic<bool> mRefreshRateChangePending = false;
 
     std::atomic<int> mEarlyTransactionFrames = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bc6e6fd..99d9bb3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -31,7 +31,6 @@
 #include <android/hardware/configstore/1.1/types.h>
 #include <android/hardware/power/Boost.h>
 #include <android/native_window.h>
-#include <android/os/BnSetInputWindowsListener.h>
 #include <android/os/IInputFlinger.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -133,6 +132,7 @@
 #include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
+#include "WindowInfosListenerInvoker.h"
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
@@ -167,6 +167,7 @@
 
 using android::hardware::power::Boost;
 using base::StringAppendF;
+using gui::IWindowInfosListener;
 using gui::WindowInfo;
 using ui::ColorMode;
 using ui::Dataspace;
@@ -275,21 +276,6 @@
 
 }  // namespace anonymous
 
-struct SetInputWindowsListener : os::BnSetInputWindowsListener {
-    explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
-
-    binder::Status onSetInputWindowsFinished() override;
-
-    std::function<void()> mListenerCb;
-};
-
-binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
-    if (mListenerCb != nullptr) {
-        mListenerCb();
-    }
-    return binder::Status::ok();
-}
-
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -355,10 +341,9 @@
         mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
-        mPowerAdvisor(*this) {
+        mPowerAdvisor(*this),
+        mWindowInfosListenerInvoker(new WindowInfosListenerInvoker(this)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
-
-    mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
 }
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -598,19 +583,11 @@
     }
 }
 
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
-                                                       ui::LayerStack layerStack) {
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
+                                                       ui::PixelFormat format) {
     if (auto& generator = mVirtualDisplayIdGenerators.hal) {
         if (const auto id = generator->generateId()) {
-            std::optional<PhysicalDisplayId> mirror;
-
-            if (const auto display = findDisplay([layerStack](const auto& display) {
-                    return !display.isVirtual() && display.getLayerStack() == layerStack;
-                })) {
-                mirror = display->getPhysicalId();
-            }
-
-            if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format, mirror)) {
+            if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
                 return *id;
             }
 
@@ -641,17 +618,14 @@
 }
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
-    const auto internalDisplayId = getInternalDisplayIdLocked();
-    if (!internalDisplayId) {
-        return {};
-    }
-
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(*internalDisplayId);
+
+    const auto internalDisplayId = getInternalDisplayIdLocked();
+    displayIds.push_back(internalDisplayId);
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
-        if (id != *internalDisplayId) {
+        if (id != internalDisplayId) {
             displayIds.push_back(id);
         }
     }
@@ -798,6 +772,8 @@
                                     ? renderengine::RenderEngine::ContextPriority::REALTIME
                                     : renderengine::RenderEngine::ContextPriority::MEDIUM)
                     .build()));
+    mMaxRenderTargetSize =
+            std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
 
     // Set SF main policy after initializing RenderEngine which has its own policy.
     if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
@@ -816,10 +792,10 @@
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
     const auto displayId = display->getPhysicalId();
     LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Internal display is disconnected.");
+                        "Primary display is disconnected.");
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -885,14 +861,6 @@
     }
 }
 
-size_t SurfaceFlinger::getMaxTextureSize() const {
-    return getRenderEngine().getMaxTextureSize();
-}
-
-size_t SurfaceFlinger::getMaxViewportDims() const {
-    return getRenderEngine().getMaxViewportDims();
-}
-
 // ----------------------------------------------------------------------------
 
 bool SurfaceFlinger::authenticateSurfaceTexture(
@@ -996,6 +964,11 @@
         return NAME_NOT_FOUND;
     }
 
+    const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+    if (!displayId) {
+        return INVALID_OPERATION;
+    }
+
     info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
 
     const auto& supportedModes = display->getSupportedModes();
@@ -1055,18 +1028,18 @@
     }
 
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    const auto displayId = display->getPhysicalId();
-    info->supportedColorModes = getDisplayColorModes(displayId);
-
+    info->supportedColorModes = getDisplayColorModes(*display);
     info->hdrCapabilities = display->getHdrCapabilities();
+
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(displayId,
+            getHwComposer().hasDisplayCapability(*displayId,
                                                  hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
     std::vector<hal::ContentType> types;
-    getHwComposer().getSupportedContentTypes(displayId, &types);
+    getHwComposer().getSupportedContentTypes(*displayId, &types);
     info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
         return type == hal::ContentType::GAME;
     });
+
     return NO_ERROR;
 }
 
@@ -1290,15 +1263,15 @@
     }).wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
-    auto modes = getHwComposer().getColorModes(displayId);
-    bool isInternalDisplay = displayId == getInternalDisplayIdLocked();
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
+    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
 
-    // If it's built-in display and the configuration claims it's not wide color capable,
+    // If the display is internal and the configuration claims it's not wide color capable,
     // filter out all wide color modes. The typical reason why this happens is that the
     // hardware is not good enough to support GPU composition of wide color, and thus the
     // OEMs choose to disable this capability.
-    if (isInternalDisplay && !hasWideColorDisplay) {
+    if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
+        !hasWideColorDisplay) {
         const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
         modes.erase(newEnd, modes.end());
     }
@@ -1322,34 +1295,41 @@
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    schedule([=]() MAIN_THREAD {
-        const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-        if (!displayId) {
-            ALOGE("Invalid display token %p", displayToken.get());
-            return;
-        }
-        const auto modes = getDisplayColorModes(*displayId);
-        bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
-        if (mode < ColorMode::NATIVE || !exists) {
-            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
-                  decodeColorMode(mode).c_str(), mode, displayToken.get());
-            return;
-        }
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    auto future = schedule([=]() MAIN_THREAD -> status_t {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
                   decodeColorMode(mode).c_str(), mode, displayToken.get());
-        } else if (display->isVirtual()) {
+            return NAME_NOT_FOUND;
+        }
+
+        if (display->isVirtual()) {
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
-        } else {
-            display->getCompositionDisplay()->setColorProfile(
-                    compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
-                                                            RenderIntent::COLORIMETRIC,
-                                                            Dataspace::UNKNOWN});
+            return INVALID_OPERATION;
         }
-    }).wait();
 
+        const auto modes = getDisplayColorModes(*display);
+        const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
+
+        if (mode < ColorMode::NATIVE || !exists) {
+            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+            return BAD_VALUE;
+        }
+
+        display->getCompositionDisplay()->setColorProfile(
+                {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+
+        return NO_ERROR;
+    });
+
+    // TODO(b/195698395): Propagate error.
+    future.wait();
     return NO_ERROR;
 }
 
@@ -1639,6 +1619,9 @@
         hdrInfoReporter = sp<HdrLayerInfoReporter>::make();
     }
     hdrInfoReporter->addListener(listener);
+
+
+    mAddingHDRLayerInfoListener = true;
     return OK;
 }
 
@@ -1763,8 +1746,8 @@
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId,
-          connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
+    const bool connected = connection == hal::Connection::CONNECTED;
+    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
 
     // Only lock if we're not on the main thread. This function is normally
     // called on a hwbinder thread, but for the primary display it's called on
@@ -1956,13 +1939,9 @@
     }
 
     if (mRefreshRateOverlaySpinner) {
-        if (Mutex::Autolock lock(mStateLock);
-            const auto display = getDefaultDisplayDeviceLocked()) {
-            if (display) {
-                display->onInvalidate();
-            } else {
-                ALOGW("%s: default display is null", __func__);
-            }
+        Mutex::Autolock lock(mStateLock);
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            display->onInvalidate();
         }
     }
 
@@ -2142,6 +2121,8 @@
             mTracing.notify("bufferLatched");
         }
     }
+
+    mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp
     mVisibleRegionsDirty = false;
 
     if (mCompositionEngine->needsAnotherUpdate()) {
@@ -2286,6 +2267,7 @@
 
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
             hdrInfoListeners;
+    bool haveNewListeners = false;
     {
         Mutex::Autolock lock(mStateLock);
         if (mFpsReporter) {
@@ -2303,37 +2285,45 @@
                 }
             }
         }
+        haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock
+        mAddingHDRLayerInfoListener = false;
     }
 
-    for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
-        HdrLayerInfoReporter::HdrLayerInfo info;
-        int32_t maxArea = 0;
-        mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
-            const auto layerFe = layer->getCompositionEngineLayerFE();
-            if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) {
-                const Dataspace transfer =
+    if (haveNewListeners || mSomeDataspaceChanged || mVisibleRegionsWereDirtyThisFrame) {
+        for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
+            HdrLayerInfoReporter::HdrLayerInfo info;
+            int32_t maxArea = 0;
+            mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+                const auto layerFe = layer->getCompositionEngineLayerFE();
+                if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) {
+                    const Dataspace transfer =
                         static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
-                const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
-                                    transfer == Dataspace::TRANSFER_HLG);
+                    const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
+                                        transfer == Dataspace::TRANSFER_HLG);
 
-                if (isHdr) {
-                    const auto* outputLayer = compositionDisplay->getOutputLayerForLayer(layerFe);
-                    if (outputLayer) {
-                        info.numberOfHdrLayers++;
-                        const auto displayFrame = outputLayer->getState().displayFrame;
-                        const int32_t area = displayFrame.width() * displayFrame.height();
-                        if (area > maxArea) {
-                            maxArea = area;
-                            info.maxW = displayFrame.width();
-                            info.maxH = displayFrame.height();
+                    if (isHdr) {
+                        const auto* outputLayer =
+                            compositionDisplay->getOutputLayerForLayer(layerFe);
+                        if (outputLayer) {
+                            info.numberOfHdrLayers++;
+                            const auto displayFrame = outputLayer->getState().displayFrame;
+                            const int32_t area = displayFrame.width() * displayFrame.height();
+                            if (area > maxArea) {
+                                maxArea = area;
+                                info.maxW = displayFrame.width();
+                                info.maxH = displayFrame.height();
+                            }
                         }
                     }
                 }
-            }
-        });
-        listener->dispatchHdrLayerInfo(info);
+            });
+            listener->dispatchHdrLayerInfo(info);
+        }
     }
 
+    mSomeDataspaceChanged = false;
+    mVisibleRegionsWereDirtyThisFrame = false;
+
     mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
     mTransactionCallbackInvoker.sendCallbacks();
 
@@ -2479,7 +2469,6 @@
     handleTransactionLocked(transactionFlags);
 
     mDebugInTransaction = 0;
-    invalidateHwcGeometry();
     // here the transaction has been committed
 }
 
@@ -2745,7 +2734,7 @@
         builder.setId(physical->id);
         builder.setConnectionType(physical->type);
     } else {
-        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.layerStack));
+        builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
     }
 
     builder.setPixels(resolution);
@@ -2856,7 +2845,7 @@
             setPowerModeInternal(display, hal::PowerMode::ON);
 
             // TODO(b/175678251) Call a listener instead.
-            if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+            if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
                 updateInternalDisplayVsyncLocked(display);
             }
         }
@@ -3056,11 +3045,11 @@
 
     if (mVisibleRegionsDirty || mInputInfoChanged) {
         mInputInfoChanged = false;
-        updateInputWindowInfo();
+        notifyWindowInfos();
     } else if (mInputWindowCommands.syncInputWindows) {
         // If the caller requested to sync input windows, but there are no
         // changes to input windows, notify immediately.
-        setInputWindowsFinished();
+        windowInfosReported();
     }
 
     for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
@@ -3075,8 +3064,8 @@
     return value;
 }
 
-void SurfaceFlinger::updateInputWindowInfo() {
-    std::vector<WindowInfo> inputInfos;
+void SurfaceFlinger::notifyWindowInfos() {
+    std::vector<WindowInfo> windowInfos;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
@@ -3085,12 +3074,10 @@
                 : nullptr;
         // When calculating the screen bounds we ignore the transparent region since it may
         // result in an unwanted offset.
-        inputInfos.push_back(layer->fillInputInfo(display));
+        windowInfos.push_back(layer->fillInputInfo(display));
     });
-
-    mInputFlinger->setInputWindows(inputInfos,
-                               mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
-                                                                     : nullptr);
+    mWindowInfosListenerInvoker->windowInfosChanged(windowInfos,
+                                                    mInputWindowCommands.syncInputWindows);
 }
 
 void SurfaceFlinger::updateCursorAsync() {
@@ -3124,9 +3111,11 @@
 
 void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
     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");
+        // If the scheduler is already initialized, this means that we received
+        // a hotplug(connected) on the primary display. In that case we should
+        // update the scheduler with the most recent display information.
+        ALOGW("Scheduler already initialized, updating instead");
+        mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
         return;
     }
     const auto currRefreshRate = display->getActiveMode()->getFps();
@@ -3134,7 +3123,7 @@
                                                                       hal::PowerMode::OFF);
 
     mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
-    mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
+    mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
     // start the EventThread
     mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this);
@@ -3357,7 +3346,7 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
                                         const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
                                         const sp<IBinder>& parentHandle,
-                                        const sp<Layer>& parentLayer, bool addToCurrentState,
+                                        const sp<Layer>& parentLayer, bool addToRoot,
                                         uint32_t* outTransformHint) {
     if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
         ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
@@ -3369,7 +3358,7 @@
     if (gbc != nullptr) {
         initialProducer = IInterface::asBinder(gbc);
     }
-    setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer);
+    setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer, addToRoot);
 
     // Create a transaction includes the initial parent and producer.
     Vector<ComposerState> states;
@@ -3412,9 +3401,10 @@
     return setTransactionFlags(flags, TransactionSchedule::Late);
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule,
+                                             const sp<IBinder>& token) {
     uint32_t old = mTransactionFlags.fetch_or(flags);
-    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, token);
     if ((old & flags) == 0) signalTransaction();
     return old;
 }
@@ -3572,7 +3562,7 @@
 
         sp<Layer> layer = nullptr;
         if (s.surface) {
-            layer = fromHandleLocked(s.surface).promote();
+            layer = fromHandle(s.surface).promote();
         } else if (s.hasBufferChanges()) {
             ALOGW("Transaction with buffer, but no Layer?");
             continue;
@@ -3634,7 +3624,7 @@
         return TransactionSchedule::Late;
     }(state.flags);
 
-    setTransactionFlags(eTransactionFlushNeeded, schedule);
+    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken);
 }
 
 void SurfaceFlinger::waitForSynchronousTransaction(
@@ -3739,7 +3729,7 @@
                 setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
                                      postTime, permissions, listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
-            if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+            if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
                 mScheduler->recordLayerHistory(layer.get(),
                                                isAutoTimestamp ? 0 : desiredPresentTime,
                                                LayerHistory::LayerUpdateType::AnimationTX);
@@ -3898,15 +3888,13 @@
     sp<Layer> layer = nullptr;
     if (s.surface) {
         if (what & layer_state_t::eLayerCreated) {
-            layer = handleLayerCreatedLocked(s.surface, privileged);
+            layer = handleLayerCreatedLocked(s.surface);
             if (layer) {
-                // put the created layer into mLayersByLocalBinderToken.
-                mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
                 flags |= eTransactionNeeded | eTraversalNeeded;
                 mLayersAdded = true;
             }
         } else {
-            layer = fromHandleLocked(s.surface).promote();
+            layer = fromHandle(s.surface).promote();
         }
     } else {
         // The client may provide us a null handle. Treat it as if the layer was removed.
@@ -4136,6 +4124,15 @@
     if (what & layer_state_t::eAutoRefreshChanged) {
         layer->setAutoRefresh(s.autoRefresh);
     }
+    if (what & layer_state_t::eTrustedOverlayChanged) {
+        if (privileged) {
+            if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
+                flags |= eTraversalNeeded;
+            }
+        } else {
+            ALOGE("Attempt to set trusted overlay without permission ACCESS_SURFACE_FLINGER");
+        }
+    }
     if (what & layer_state_t::eStretchChanged) {
         if (layer->setStretchEffect(s.stretchEffect)) {
             flags |= eTraversalNeeded;
@@ -4176,17 +4173,30 @@
     }
     bool bufferChanged = what & layer_state_t::eBufferChanged;
     bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
+    bool bufferSizeExceedsLimit = false;
     std::shared_ptr<renderengine::ExternalTexture> buffer;
     if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
-        ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
-        buffer = ClientCache::getInstance().get(s.cachedBuffer);
+        bufferSizeExceedsLimit =
+                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+            buffer = ClientCache::getInstance().get(s.cachedBuffer);
+        }
     } else if (cacheIdChanged) {
         buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (bufferChanged && s.buffer != nullptr) {
-        buffer = std::make_shared<
-                renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
-                                               renderengine::ExternalTexture::Usage::READABLE);
+        bufferSizeExceedsLimit =
+                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            buffer = std::make_shared<
+                    renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+                                                   renderengine::ExternalTexture::Usage::READABLE);
+        }
     }
+    ALOGE_IF(bufferSizeExceedsLimit,
+             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+             "limit.",
+             layer->getDebugName());
     if (buffer) {
         const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
         const uint64_t frameNumber = frameNumberChanged
@@ -4225,7 +4235,7 @@
 
     {
         Mutex::Autolock _l(mStateLock);
-        mirrorFrom = fromHandleLocked(mirrorFromHandle).promote();
+        mirrorFrom = fromHandle(mirrorFromHandle).promote();
         if (!mirrorFrom) {
             return NAME_NOT_FOUND;
         }
@@ -4308,9 +4318,9 @@
         return result;
     }
 
-    bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
-    result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
-                            addToCurrentState, outTransformHint);
+    bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
+    result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
+                            outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
@@ -4423,7 +4433,7 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
     Mutex::Autolock lock(mStateLock);
     // If a layer has a parent, we allow it to out-live it's handle
     // with the idea that the parent holds a reference and will eventually
@@ -4434,17 +4444,7 @@
         mCurrentState.layersSortedByZ.remove(layer);
     }
     markLayerPendingRemovalLocked(layer);
-
-    auto it = mLayersByLocalBinderToken.begin();
-    while (it != mLayersByLocalBinderToken.end()) {
-        if (it->second == layer) {
-            mBufferCountTracker.remove(it->first->localBinder());
-            it = mLayersByLocalBinderToken.erase(it);
-        } else {
-            it++;
-        }
-    }
-
+    mBufferCountTracker.remove(handle);
     layer.clear();
 }
 
@@ -4537,8 +4537,7 @@
     if (currentMode == hal::PowerMode::OFF) {
         const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
         if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
-            mActiveDisplayToken = display->getDisplayToken();
-            onActiveDisplayChangedLocked(getDisplayDeviceLocked(mActiveDisplayToken));
+            onActiveDisplayChangedLocked(display);
         }
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
         // We can merge the syscall later.
@@ -5327,6 +5326,14 @@
             }
             return PERMISSION_DENIED;
         }
+        case ADD_WINDOW_INFOS_LISTENER:
+        case REMOVE_WINDOW_INFOS_LISTENER: {
+            const int uid = IPCThreadState::self()->getCallingUid();
+            if (uid == AID_SYSTEM || uid == AID_GRAPHICS) {
+                return OK;
+            }
+            return PERMISSION_DENIED;
+        }
     }
 
     // These codes are used for the IBinder protocol to either interrogate the recipient
@@ -5689,12 +5696,10 @@
             // Inject a hotplug connected event for the primary display. This will deallocate and
             // reallocate the display state including framebuffers.
             case 1037: {
-                std::optional<hal::HWDisplayId> hwcId;
-                {
-                    Mutex::Autolock lock(mStateLock);
-                    hwcId = getHwComposer().getInternalHwcDisplayId();
-                }
-                onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED);
+                const hal::HWDisplayId hwcId =
+                        (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
+
+                onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
                 return NO_ERROR;
             }
             // Modify the max number of display frames stored within FrameTimeline
@@ -6057,7 +6062,7 @@
     {
         Mutex::Autolock lock(mStateLock);
 
-        parent = fromHandleLocked(args.layerHandle).promote();
+        parent = fromHandle(args.layerHandle).promote();
         if (parent == nullptr || parent->isRemovedFromCurrentState()) {
             ALOGE("captureLayers called with an invalid or removed parent");
             return NAME_NOT_FOUND;
@@ -6088,7 +6093,7 @@
         reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
 
         for (const auto& handle : args.excludeHandles) {
-            sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
+            sp<Layer> excludeLayer = fromHandle(handle).promote();
             if (excludeLayer != nullptr) {
                 excludeLayers.emplace(excludeLayer);
             } else {
@@ -6170,6 +6175,13 @@
                                              const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
+    if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
+        ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
+              ") that exceeds render target size limit.",
+              bufferSize.getWidth(), bufferSize.getHeight());
+        return BAD_VALUE;
+    }
+
     // Loop over all visible layers to see whether there's any protected layer. A protected layer is
     // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
     // A protected layer has no implication on whether it's secure, which is explicitly set by
@@ -6381,7 +6393,7 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::setInputWindowsFinished() {
+void SurfaceFlinger::windowInfosReported() {
     Mutex::Autolock _l(mStateLock);
     signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
 }
@@ -6552,24 +6564,8 @@
     return NO_ERROR;
 }
 
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
-    Mutex::Autolock _l(mStateLock);
-    return fromHandleLocked(handle);
-}
-
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
-    BBinder* b = nullptr;
-    if (handle) {
-        b = handle->localBinder();
-    }
-    if (b == nullptr) {
-        return nullptr;
-    }
-    auto it = mLayersByLocalBinderToken.find(b);
-    if (it != mLayersByLocalBinderToken.end()) {
-        return it->second;
-    }
-    return nullptr;
+wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
+    return Layer::fromHandle(handle);
 }
 
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
@@ -6785,33 +6781,29 @@
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
-    const auto maxSupportedRefreshRate = [&] {
-        const auto display = getDefaultDisplayDevice();
-        if (display) {
-            return display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+    Fps maxRefreshRate(60.f);
+
+    if (!getHwComposer().isHeadless()) {
+        if (const auto display = getDefaultDisplayDevice()) {
+            maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
         }
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
-    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate);
+    }
+
+    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxRefreshRate);
     return NO_ERROR;
 }
 
 int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
-    const auto refreshRate = [&] {
-        const auto frameRateOverride = mScheduler->getFrameRateOverride(uid);
-        if (frameRateOverride.has_value()) {
-            return frameRateOverride.value();
-        }
+    Fps refreshRate(60.f);
 
-        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-        if (display) {
-            return display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+    if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) {
+        refreshRate = *frameRateOverride;
+    } else if (!getHwComposer().isHeadless()) {
+        if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
+            refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
         }
+    }
 
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
     return getMaxAcquiredBufferCountForRefreshRate(refreshRate);
 }
 
@@ -6832,10 +6824,10 @@
 
 void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
                                           const wp<IBinder>& parent, const wp<Layer> parentLayer,
-                                          const wp<IBinder>& producer) {
+                                          const wp<IBinder>& producer, bool addToRoot) {
     Mutex::Autolock lock(mCreatedLayersLock);
     mCreatedLayers[handle->localBinder()] =
-            std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer);
+            std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer, addToRoot);
 }
 
 auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
@@ -6860,7 +6852,7 @@
     return state;
 }
 
-sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged) {
+sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) {
     const auto& state = getLayerCreatedState(handle);
     if (!state) {
         return nullptr;
@@ -6873,9 +6865,9 @@
     }
 
     sp<Layer> parent;
-    bool allowAddRoot = privileged;
+    bool allowAddRoot = state->addToRoot;
     if (state->initialParent != nullptr) {
-        parent = fromHandleLocked(state->initialParent.promote()).promote();
+        parent = fromHandle(state->initialParent.promote()).promote();
         if (parent == nullptr) {
             ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
             allowAddRoot = false;
@@ -6935,16 +6927,35 @@
 void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
     ATRACE_CALL();
 
+    if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+        display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+    }
+
     if (!activeDisplay) {
         ALOGE("%s: activeDisplay is null", __func__);
         return;
     }
+    mActiveDisplayToken = activeDisplay->getDisplayToken();
+
+    activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
     mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
     onActiveDisplaySizeChanged(activeDisplay);
 }
 
+status_t SurfaceFlinger::addWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) const {
+    mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) const {
+    mWindowInfosListenerInvoker->removeWindowInfosListener(windowInfosListener);
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 80f3f9c..0ac6438 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -93,7 +93,6 @@
 class TunnelModeEnabledReporter;
 class HdrLayerInfoReporter;
 class HWComposer;
-struct SetInputWindowsListener;
 class IGraphicBufferProducer;
 class Layer;
 class MessageBase;
@@ -102,6 +101,7 @@
 class RenderArea;
 class TimeStats;
 class FrameTracer;
+class WindowInfosListenerInvoker;
 
 using gui::ScreenCaptureResults;
 
@@ -327,13 +327,12 @@
     // Returns nullptr if the handle does not point to an existing layer.
     // Otherwise, returns a weak reference so that callers off the main-thread
     // won't accidentally hold onto the last strong reference.
-    wp<Layer> fromHandle(const sp<IBinder>& handle);
-    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
+    wp<Layer> fromHandle(const sp<IBinder>& handle) const;
 
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
-    void setInputWindowsFinished();
+    void windowInfosReported();
 
     // Disables expensive rendering for all displays
     // This is scheduled on the main thread
@@ -358,6 +357,15 @@
     // display.
     virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
 
+    virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+            REQUIRES(mStateLock);
+
+    // Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
+    template <typename Predicate>
+    bool hasDisplay(Predicate p) const REQUIRES(mStateLock) {
+        return static_cast<bool>(findDisplay(p));
+    }
+
 private:
     friend class BufferLayer;
     friend class BufferQueueLayer;
@@ -715,6 +723,11 @@
 
     status_t getMaxAcquiredBufferCount(int* buffers) const override;
 
+    status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+    status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -805,7 +818,7 @@
     void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
 
     void updateInputFlinger();
-    void updateInputWindowInfo();
+    void notifyWindowInfos();
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
@@ -845,7 +858,7 @@
     // but there is no need to try and wake up immediately to do it. Rather we rely on
     // onFrameAvailable or another layer update to wake us up.
     void setTraversalNeeded();
-    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
+    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule, const sp<IBinder>& = {});
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
@@ -892,14 +905,14 @@
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
     // resources associated to this layer.
-    void onHandleDestroyed(sp<Layer>& layer);
+    void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
     void markLayerPendingRemovalLocked(const sp<Layer>& layer);
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
                             const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
                             const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
-                            bool addToCurrentState, uint32_t* outTransformHint);
+                            bool addToRoot, uint32_t* outTransformHint);
 
     // Traverse through all the layers and compute and cache its bounds.
     void computeLayerBounds();
@@ -925,8 +938,9 @@
 
     void readPersistentProperties();
 
-    size_t getMaxTextureSize() const;
-    size_t getMaxViewportDims() const;
+    bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
+        return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
+    }
 
     int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
 
@@ -1042,8 +1056,6 @@
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
     void processDisplayChangesLocked() REQUIRES(mStateLock);
-    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
-            REQUIRES(mStateLock);
     void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
@@ -1106,23 +1118,26 @@
         return {};
     }
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): SF conflates the primary (a.k.a. default) display with the first display
+    // connected at boot, which is typically internal. (Theoretically, it must be internal because
+    // SF does not support disconnecting it, though in practice HWC may circumvent this limitation.)
+    //
+    // SF inherits getInternalDisplayToken and getInternalDisplayId from ISurfaceComposer, so these
+    // locked counterparts are named consistently. Once SF supports headless mode and can designate
+    // any display as primary, the "internal" misnomer will be phased out.
     sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
-        const auto displayId = getInternalDisplayIdLocked();
-        return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
+        return getPhysicalDisplayTokenLocked(getInternalDisplayIdLocked());
     }
 
-    std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
-        const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
-        return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+    PhysicalDisplayId getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+        return getHwComposer().getPrimaryDisplayId();
     }
 
     // Toggles use of HAL/GPU virtual displays.
     void enableHalVirtualDisplays(bool);
 
     // Virtual display lifecycle for ID generation and HAL allocation.
-    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, ui::LayerStack)
-            REQUIRES(mStateLock);
+    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
     void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
@@ -1194,8 +1209,7 @@
     /*
      * Misc
      */
-    std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
-            REQUIRES(mStateLock);
+    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
 
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
@@ -1249,11 +1263,19 @@
     State mDrawingState{LayerVector::StateSet::Drawing};
     bool mVisibleRegionsDirty = false;
 
+    // VisibleRegions dirty is already cleared by postComp, but we need to track it to prevent
+    // extra work in the HDR layer info listener.
+    bool mVisibleRegionsWereDirtyThisFrame = false;
+    // Used to ensure we omit a callback when HDR layer info listener is newly added but the
+    // scene hasn't changed
+    bool mAddingHDRLayerInfoListener = false;
+
     // Set during transaction application stage to track if the input info or children
     // for a layer has changed.
     // TODO: Also move visibleRegions over to a boolean system.
     bool mInputInfoChanged = false;
     bool mSomeChildrenChanged;
+    bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
     bool mGeometryInvalid = false;
@@ -1291,8 +1313,6 @@
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
     } mVirtualDisplayIdGenerators;
 
-    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
-
     // don't use a lock for these, we don't care
     int mDebugRegion = 0;
     bool mDebugDisableHWC = false;
@@ -1382,6 +1402,9 @@
 
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+    // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
+    // any mutex.
+    size_t mMaxRenderTargetSize{1};
 
     const std::string mHwcServiceName;
 
@@ -1398,7 +1421,7 @@
     std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
 
     // Optional to defer construction until PhaseConfiguration is created.
-    std::optional<scheduler::VsyncModulator> mVsyncModulator;
+    sp<VsyncModulator> mVsyncModulator;
 
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
@@ -1422,8 +1445,6 @@
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
 
-    sp<SetInputWindowsListener> mSetInputWindowsListener;
-
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
     // This should only be accessed on the main thread.
@@ -1451,11 +1472,12 @@
     mutable Mutex mCreatedLayersLock;
     struct LayerCreatedState {
         LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
-                          const wp<Layer> parentLayer, const wp<IBinder>& producer)
+                          const wp<Layer> parentLayer, const wp<IBinder>& producer, bool addToRoot)
               : layer(layer),
                 initialParent(parent),
                 initialParentLayer(parentLayer),
-                initialProducer(producer) {}
+                initialProducer(producer),
+                addToRoot(addToRoot) {}
         wp<Layer> layer;
         // Indicates the initial parent of the created layer, only used for creating layer in
         // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
@@ -1464,6 +1486,10 @@
         // Indicates the initial graphic buffer producer of the created layer, only used for
         // creating layer in SurfaceFlinger.
         wp<IBinder> initialProducer;
+        // Indicates whether the layer getting created should be added at root if there's no parent
+        // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
+        // be added offscreen.
+        bool addToRoot;
     };
 
     // A temporay pool that store the created layers and will be added to current state in main
@@ -1471,10 +1497,9 @@
     std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
     void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
                               const wp<IBinder>& parent, const wp<Layer> parentLayer,
-                              const wp<IBinder>& producer);
+                              const wp<IBinder>& producer, bool addToRoot);
     auto getLayerCreatedState(const sp<IBinder>& handle);
-    sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged)
-            REQUIRES(mStateLock);
+    sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
 
     std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
 
@@ -1489,6 +1514,8 @@
     }
 
     wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
+
+    const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 2084bb6..9be3abe 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -149,6 +149,7 @@
                             getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf),
                             layer->mDrawingState.z);
     addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius);
+    addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay);
 }
 
 void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -183,12 +184,9 @@
     return NO_ERROR;
 }
 
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const {
-    const sp<const IBinder>& handle(weakHandle.promote());
-    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
-    const sp<const Layer> layer(layerHandle->owner.promote());
-    // layer could be a nullptr at this point
-    return layer;
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
+    sp<IBinder> handle = weakHandle.promote();
+    return Layer::fromHandle(handle).promote();
 }
 
 int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
@@ -203,12 +201,11 @@
     return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
 }
 
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const {
+int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
     if (handle == nullptr) {
         return -1;
     }
-    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
-    const sp<const Layer> layer(layerHandle->owner.promote());
+    const sp<const Layer> layer = Layer::fromHandle(handle).promote();
     return layer == nullptr ? -1 : getLayerId(layer);
 }
 
@@ -398,6 +395,13 @@
     overrideChange->set_radius(shadowRadius);
 }
 
+void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId,
+                                                 bool isTrustedOverlay) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay());
+    overrideChange->set_is_trusted_overlay(isTrustedOverlay);
+}
+
 void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
         const layer_state_t& state)
 {
@@ -461,6 +465,9 @@
     if (state.what & layer_state_t::eShadowRadiusChanged) {
         addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
     }
+    if (state.what & layer_state_t::eTrustedOverlayChanged) {
+        addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay);
+    }
     if (state.what & layer_state_t::eStretchChanged) {
         ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
     }
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index e498e7d..7b331b9 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -48,7 +48,7 @@
 using Increment = surfaceflinger::Increment;
 using DisplayChange = surfaceflinger::DisplayChange;
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 class SurfaceInterceptor : public IBinder::DeathRecipient {
 public:
@@ -133,10 +133,10 @@
     void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
 
     status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const;
+    const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
     int32_t getLayerId(const sp<const Layer>& layer) const;
     int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
-    int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const;
+    int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
 
     Increment* createTraceIncrementLocked();
     void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
@@ -177,6 +177,7 @@
     void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
                                  int z);
     void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
+    void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay);
 
     // Add display transactions to the trace
     DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 15a503d..42a16c6 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -77,7 +77,7 @@
 private:
     class Runner;
     static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
-    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
+    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
 
     SurfaceFlinger& mFlinger;
     mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
new file mode 100644
index 0000000..dc2aa58
--- /dev/null
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WindowInfosListenerInvoker.h"
+#include <gui/ISurfaceComposer.h>
+#include <unordered_set>
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using gui::IWindowInfosListener;
+using gui::WindowInfo;
+
+struct WindowInfosReportedListener : gui::BnWindowInfosReportedListener {
+    explicit WindowInfosReportedListener(std::function<void()> listenerCb)
+          : mListenerCb(listenerCb) {}
+
+    binder::Status onWindowInfosReported() override {
+        if (mListenerCb != nullptr) {
+            mListenerCb();
+        }
+        return binder::Status::ok();
+    }
+
+    std::function<void()> mListenerCb;
+};
+
+WindowInfosListenerInvoker::WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf) : mSf(sf) {
+    mWindowInfosReportedListener =
+            new WindowInfosReportedListener([&]() { windowInfosReported(); });
+}
+
+void WindowInfosListenerInvoker::addWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) {
+    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
+
+    asBinder->linkToDeath(this);
+    std::scoped_lock lock(mListenersMutex);
+    mWindowInfosListeners.emplace(asBinder, windowInfosListener);
+}
+
+void WindowInfosListenerInvoker::removeWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) {
+    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
+
+    std::scoped_lock lock(mListenersMutex);
+    asBinder->unlinkToDeath(this);
+    mWindowInfosListeners.erase(asBinder);
+}
+
+void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mListenersMutex);
+    mWindowInfosListeners.erase(who);
+}
+
+void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+                                                    bool shouldSync) {
+    std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>>
+            windowInfosListeners;
+
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (const auto& [_, listener] : mWindowInfosListeners) {
+            windowInfosListeners.insert(listener);
+        }
+    }
+
+    mCallbacksPending = windowInfosListeners.size();
+
+    for (const auto& listener : windowInfosListeners) {
+        listener->onWindowInfosChanged(windowInfos,
+                                       shouldSync ? mWindowInfosReportedListener : nullptr);
+    }
+}
+
+void WindowInfosListenerInvoker::windowInfosReported() {
+    mCallbacksPending--;
+    if (mCallbacksPending == 0) {
+        mSf->windowInfosReported();
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
new file mode 100644
index 0000000..5e5796f
--- /dev/null
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/IWindowInfosListener.h>
+#include <android/gui/IWindowInfosReportedListener.h>
+#include <binder/IBinder.h>
+#include <utils/Mutex.h>
+#include <unordered_map>
+
+namespace android {
+
+class SurfaceFlinger;
+
+class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+public:
+    WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf);
+    void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+    void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+
+    void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync);
+
+protected:
+    void binderDied(const wp<IBinder>& who) override;
+
+private:
+    void windowInfosReported();
+
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    const sp<SurfaceFlinger> mSf;
+    std::mutex mListenersMutex;
+    std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
+            mWindowInfosListeners GUARDED_BY(mListenersMutex);
+    sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
+    std::atomic<size_t> mCallbacksPending{0};
+};
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index aef670d..2841f7c 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -105,6 +105,7 @@
     layer.queuedFrames = layerProto.queued_frames();
     layer.refreshPending = layerProto.refresh_pending();
     layer.isProtected = layerProto.is_protected();
+    layer.isTrustedOverlay = layerProto.is_trusted_overlay();
     layer.cornerRadius = layerProto.corner_radius();
     layer.backgroundBlurRadius = layerProto.background_blur_radius();
     for (const auto& entry : layerProto.metadata()) {
@@ -289,6 +290,7 @@
     StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
     StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
     StringAppendF(&result, "isProtected=%1d, ", isProtected);
+    StringAppendF(&result, "isTrustedOverlay=%1d, ", isTrustedOverlay);
     StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
     StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
     StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index c48354f..52503ba 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -109,6 +109,7 @@
         int32_t queuedFrames;
         bool refreshPending;
         bool isProtected;
+        bool isTrustedOverlay;
         float cornerRadius;
         int backgroundBlurRadius;
         LayerMetadata metadata;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 9f25674..dddc677 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -128,6 +128,8 @@
 
   // Regions of a layer, where blur should be applied.
   repeated BlurRegion blur_regions = 54;
+
+  bool is_trusted_overlay = 55;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index b96725f..32ad873 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -51,6 +51,7 @@
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
         "VirtualDisplay_test.cpp",
+        "WindowInfosListener_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
@@ -59,7 +60,7 @@
         "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk",
         "android.hardware.graphics.common@1.2",
         "libandroid",
         "libbase",
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index ccf434d..d027865 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -18,7 +18,9 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#include <private/android_filesystem_config.h>
 #include "LayerTransactionTest.h"
+#include "utils/TransactionUtils.h"
 
 namespace android {
 
@@ -227,6 +229,50 @@
     }
 }
 
+// Test that the mirror layer is initially offscreen.
+TEST_F(MirrorLayerTest, InitialMirrorState) {
+    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    ui::DisplayMode mode;
+    SurfaceComposerClient::getActiveDisplayMode(display, &mode);
+    const ui::Size& size = mode.resolution;
+
+    sp<SurfaceControl> mirrorLayer = nullptr;
+    {
+        // Run as system to get the ACCESS_SURFACE_FLINGER permission when mirroring
+        UIDFaker f(AID_SYSTEM);
+        // Mirror mChildLayer
+        mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+        ASSERT_NE(mirrorLayer, nullptr);
+    }
+
+    // Show the mirror layer, but don't reparent to a layer on screen.
+    Transaction()
+            .setPosition(mirrorLayer, 500, 500)
+            .show(mirrorLayer)
+            .setLayer(mirrorLayer, INT32_MAX - 1)
+            .apply();
+
+    {
+        SCOPED_TRACE("Offscreen Mirror");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, size.getWidth(), 50), Color::RED);
+        shot->expectColor(Rect(0, 0, 50, size.getHeight()), Color::RED);
+        shot->expectColor(Rect(450, 0, size.getWidth(), size.getHeight()), Color::RED);
+        shot->expectColor(Rect(0, 450, size.getWidth(), size.getHeight()), Color::RED);
+        shot->expectColor(Rect(50, 50, 450, 450), Color::GREEN);
+    }
+
+    // Add mirrorLayer as child of mParentLayer so it's shown on the display
+    Transaction().reparent(mirrorLayer, mParentLayer).apply();
+
+    {
+        SCOPED_TRACE("On Screen Mirror");
+        auto shot = screenshot();
+        // Child mirror
+        shot->expectColor(Rect(550, 550, 950, 950), Color::GREEN);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 6912fcf..ab2064e 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -515,7 +515,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+                                              ISurfaceComposerClient::eFXSurfaceBufferState);
 
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
 
@@ -532,6 +533,21 @@
     ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
+TEST_F(ScreenCaptureTest, CaptureTooLargeLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60);
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+    Transaction().show(redLayer).setLayer(redLayer, INT32_MAX).apply(true);
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = redLayer->getHandle();
+    captureArgs.frameScaleX = INT32_MAX / 60;
+    captureArgs.frameScaleY = INT32_MAX / 60;
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults));
+}
+
 TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
     sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
                                               ISurfaceComposerClient::eFXSurfaceBufferState);
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index ee4e863..a424059 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -61,7 +61,7 @@
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
 constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
@@ -193,6 +193,7 @@
     bool reparentUpdateFound(const SurfaceChange& change, bool found);
     bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
     bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
+    bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found);
     bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
 
     // Find all of the updates in the single trace
@@ -228,6 +229,7 @@
     void reparentUpdate(Transaction&);
     void relativeParentUpdate(Transaction&);
     void shadowRadiusUpdate(Transaction&);
+    void trustedOverlayUpdate(Transaction&);
     void surfaceCreation(Transaction&);
     void displayCreation(Transaction&);
     void displayDeletion(Transaction&);
@@ -405,6 +407,10 @@
     t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
 }
 
+void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) {
+    t.setTrustedOverlay(mBGSurfaceControl, true);
+}
+
 void SurfaceInterceptorTest::displayCreation(Transaction&) {
     sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
     SurfaceComposerClient::destroyDisplay(testDisplay);
@@ -433,6 +439,7 @@
     runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
     runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
     runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
+    runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate);
 }
 
 void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -644,6 +651,17 @@
     return foundShadowRadius;
 }
 
+bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change,
+                                                       bool foundTrustedOverlay) {
+    bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay());
+    if (hasTrustedOverlay && !foundTrustedOverlay) {
+        foundTrustedOverlay = true;
+    } else if (hasTrustedOverlay && foundTrustedOverlay) {
+        []() { FAIL(); }();
+    }
+    return foundTrustedOverlay;
+}
+
 bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
         SurfaceChange::SurfaceChangeCase changeCase) {
     bool foundUpdate = false;
@@ -704,6 +722,9 @@
                         case SurfaceChange::SurfaceChangeCase::kShadowRadius:
                             foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kTrustedOverlay:
+                            foundUpdate = trustedOverlayUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
                             break;
                     }
@@ -897,6 +918,11 @@
                 SurfaceChange::SurfaceChangeCase::kShadowRadius);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate,
+                SurfaceChange::SurfaceChangeCase::kTrustedOverlay);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
     captureTest(&SurfaceInterceptorTest::runAllUpdates,
                 &SurfaceInterceptorTest::assertAllUpdatesFound);
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
new file mode 100644
index 0000000..89228d5
--- /dev/null
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/android_filesystem_config.h>
+#include <future>
+#include "utils/TransactionUtils.h"
+
+namespace android {
+using Transaction = SurfaceComposerClient::Transaction;
+using gui::WindowInfo;
+
+class WindowInfosListenerTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        seteuid(AID_SYSTEM);
+        mClient = new SurfaceComposerClient;
+        mWindowInfosListener = new SyncWindowInfosListener();
+        mClient->addWindowInfosListener(mWindowInfosListener);
+    }
+
+    void TearDown() override {
+        mClient->removeWindowInfosListener(mWindowInfosListener);
+        seteuid(AID_ROOT);
+    }
+
+    struct SyncWindowInfosListener : public gui::WindowInfosListener {
+    public:
+        void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+            windowInfosPromise.set_value(windowInfos);
+        }
+
+        std::vector<WindowInfo> waitForWindowInfos() {
+            std::future<std::vector<WindowInfo>> windowInfosFuture =
+                    windowInfosPromise.get_future();
+            std::vector<WindowInfo> windowInfos = windowInfosFuture.get();
+            windowInfosPromise = std::promise<std::vector<WindowInfo>>();
+            return windowInfos;
+        }
+
+    private:
+        std::promise<std::vector<WindowInfo>> windowInfosPromise;
+    };
+
+    sp<SurfaceComposerClient> mClient;
+    sp<SyncWindowInfosListener> mWindowInfosListener;
+};
+
+std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
+                                                 std::vector<WindowInfo> windowInfos) {
+    std::optional<WindowInfo> foundWindowInfo = std::nullopt;
+    for (WindowInfo windowInfo : windowInfos) {
+        if (windowInfo.token == targetWindowInfo.token) {
+            foundWindowInfo = std::make_optional<>(windowInfo);
+            break;
+        }
+    }
+
+    return foundWindowInfo;
+}
+
+TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
+    std::string name = "Test Layer";
+    sp<IBinder> token = new BBinder();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+
+    Transaction()
+            .setLayerStack(surfaceControl, 0)
+            .show(surfaceControl)
+            .setLayer(surfaceControl, INT32_MAX - 1)
+            .setInputWindowInfo(surfaceControl, windowInfo)
+            .apply();
+
+    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
+    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+
+    Transaction().reparent(surfaceControl, nullptr).apply();
+
+    windowInfos = mWindowInfosListener->waitForWindowInfos();
+    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_EQ(std::nullopt, foundWindowInfo);
+}
+
+TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
+    std::string name = "Test Layer";
+    sp<IBinder> token = new BBinder();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+
+    Transaction()
+            .setLayerStack(surfaceControl, 0)
+            .show(surfaceControl)
+            .setLayer(surfaceControl, INT32_MAX - 1)
+            .setInputWindowInfo(surfaceControl, windowInfo)
+            .apply();
+
+    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
+    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+    ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty());
+
+    Rect touchableRegions(0, 0, 50, 50);
+    windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
+    Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
+
+    windowInfos = mWindowInfosListener->waitForWindowInfos();
+    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+    ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index fca3bfc..98c1889 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -108,6 +108,8 @@
 
         mComposer = new Hwc2::mock::Composer();
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+        mFlinger.mutableMaxRenderTargetSize() = 16384;
     }
 
     ~CompositionTest() {
@@ -519,8 +521,6 @@
 
     static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
         // TODO: Eliminate the complexity of actually creating a buffer
-        EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384));
-        EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384));
         status_t err =
                 layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                                   LayerProperties::FORMAT);
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index dc04b6d..cd4a5c9 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -25,6 +25,7 @@
 #include <gtest/gtest.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/Hash.h"
 
 using ::testing::ElementsAre;
 
@@ -134,7 +135,7 @@
 }
 
 uint32_t hash(const char* str) {
-    return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+    return static_cast<uint32_t>(cityHash64Len0To16(str));
 }
 
 } // namespace
@@ -309,9 +310,9 @@
     ASSERT_TRUE(tertiaryInfo);
 
     // Display IDs should be unique.
-    EXPECT_NE(primaryInfo->id, secondaryInfo->id);
-    EXPECT_NE(primaryInfo->id, tertiaryInfo->id);
-    EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
+    EXPECT_EQ(4633257497453176576, primaryInfo->id.value);
+    EXPECT_EQ(4621520285560261121, secondaryInfo->id.value);
+    EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value);
 }
 
 TEST(DisplayIdentificationTest, deviceProductInfo) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 6ce281d..de058a4 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -615,8 +615,8 @@
     }
 
     static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<4>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+                .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 8e7554b..a4e9d20 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -487,5 +487,40 @@
     EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate));
 }
 
+TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    const auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    const auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    const auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    const auto childOfChild1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, childOfChild1);
+
+    childOfChild1->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    addChild(parent, child2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, childOfChild1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
index b713334..313ab03 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -282,7 +282,8 @@
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
-    processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+    EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
@@ -290,96 +291,106 @@
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-    // A hotplug disconnect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+                // HWComposer should not have an entry for the display
+                EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+                // SF should not have a display token.
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // The display is already completely set up.
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
+                // The display is already completely set up.
+                Case::Display::injectHwcDisplay(this);
+                auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+                existing.inject();
 
-    // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-    // A hotplug connect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
-    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+                // The existing token should have been removed
+                verifyDisplayIsNotConnected(existing.token());
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+                EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
 
-    // A new display should be connected in its place
+                // A new display should be connected in its place
 
-    verifyPhysicalDisplayIsConnected<Case>();
+                verifyPhysicalDisplayIsConnected<Case>();
 
-    // --------------------------------------------------------------------
-    // Cleanup conditions
+                // --------------------------------------------------------------------
+                // Cleanup conditions
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
 TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index e32c4bf..7ead0af 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -292,7 +292,9 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
-    setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+    // External displays must be secondary, as the primary display cannot be disconnected.
+    EXPECT_EXIT(setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Missing primary display");
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index ad284e3..e036f4d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -227,7 +227,8 @@
         mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
         const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
-        mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
+        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
@@ -434,11 +435,11 @@
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
+    auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
 
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
-    auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
-    auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+    auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
     auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
 
     auto fromHandle(const sp<IBinder>& handle) {
@@ -598,13 +599,12 @@
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
                 if (mIsPrimary) {
-                    flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+                    flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
                 } else {
-                    // If there is an external HWC display there should always be an internal ID
+                    // If there is an external HWC display, there should always be a primary ID
                     // as well. Set it to some arbitrary value.
-                    auto& internalId = flinger->mutableInternalHwcDisplayId();
-                    if (!internalId) internalId = mHwcDisplayId - 1;
-                    flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+                    auto& primaryId = flinger->mutablePrimaryHwcDisplayId();
+                    if (!primaryId) primaryId = mHwcDisplayId - 1;
                 }
             }
         }
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d59d64b..ddc02bf 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -942,6 +942,27 @@
     EXPECT_THAT(cb.mReadyTime[0], 970);
 }
 
+TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+
+    mDispatch.schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch.schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+
+    advanceToNextCallback();
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(2000));
+    ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+    EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
+    ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+    EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
+}
+
 class VSyncDispatchTimerQueueEntryTest : public testing::Test {
 protected:
     nsecs_t const mPeriod = 1000;
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index 60952bf..b519582 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <binder/Binder.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -21,6 +22,13 @@
 
 namespace android::scheduler {
 
+class TestableVsyncModulator : public VsyncModulator {
+public:
+    TestableVsyncModulator(const VsyncConfigSet& config, Now now) : VsyncModulator(config, now) {}
+
+    void binderDied(const wp<IBinder>& token) { VsyncModulator::binderDied(token); }
+};
+
 class VsyncModulatorTest : public testing::Test {
     enum {
         SF_OFFSET_LATE,
@@ -60,30 +68,31 @@
 
     const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate,
                                                      nanos(HWC_MIN_WORK_DURATION)};
-    VsyncModulator mVsyncModulator{mOffsets, Now};
+    sp<TestableVsyncModulator> mVsyncModulator = sp<TestableVsyncModulator>::make(mOffsets, Now);
 
-    void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
+    void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator->setVsyncConfigSet(mOffsets)); }
 };
 
-#define CHECK_COMMIT(result, configs)                         \
-    EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
-    EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
+#define CHECK_COMMIT(result, configs)                          \
+    EXPECT_EQ(result, mVsyncModulator->onTransactionCommit()); \
+    EXPECT_EQ(configs, mVsyncModulator->getVsyncConfig());
 
-#define CHECK_REFRESH(N, result, configs)                           \
-    for (int i = 0; i < N; i++) {                                   \
-        EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
-        EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());       \
+#define CHECK_REFRESH(N, result, configs)                            \
+    for (int i = 0; i < N; i++) {                                    \
+        EXPECT_EQ(result, mVsyncModulator->onDisplayRefresh(false)); \
+        EXPECT_EQ(configs, mVsyncModulator->getVsyncConfig());       \
     }
 
 TEST_F(VsyncModulatorTest, Late) {
-    EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+    EXPECT_FALSE(mVsyncModulator->setTransactionSchedule(Schedule::Late));
 
     CHECK_COMMIT(std::nullopt, kLate);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
 }
 
 TEST_F(VsyncModulatorTest, EarlyEnd) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
@@ -91,12 +100,13 @@
 }
 
 TEST_F(VsyncModulatorTest, EarlyStart) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
@@ -104,16 +114,17 @@
 }
 
 TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token));
 
     CHECK_COMMIT(kEarly, kEarly);
 
     for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
-        EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+        EXPECT_FALSE(mVsyncModulator->setTransactionSchedule(Schedule::Late));
         CHECK_REFRESH(1, std::nullopt, kEarly);
     }
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
@@ -121,18 +132,19 @@
 }
 
 TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(1, kEarly, kEarly);
     CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
@@ -140,26 +152,64 @@
 }
 
 TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(1, kEarly, kEarly);
 
     for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
-        EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+        EXPECT_FALSE(mVsyncModulator->setTransactionSchedule(Schedule::Late));
         CHECK_REFRESH(1, std::nullopt, kEarly);
     }
 
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token));
 
     CHECK_COMMIT(kEarly, kEarly);
     CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
     CHECK_REFRESH(1, kLate, kLate);
 }
 
+TEST_F(VsyncModulatorTest, EarlyStartDifferentClients) {
+    const auto token1 = sp<BBinder>::make();
+    const auto token2 = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token1));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token2));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token1));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyEnd, token2));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+    CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithBinderDeath) {
+    const auto token = sp<BBinder>::make();
+    EXPECT_EQ(kEarly, mVsyncModulator->setTransactionSchedule(Schedule::EarlyStart, token));
+
+    CHECK_COMMIT(kEarly, kEarly);
+    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+    mVsyncModulator->binderDied(token);
+
+    CHECK_COMMIT(std::nullopt, kLate);
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index cb3bd73..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -54,8 +54,7 @@
     MOCK_METHOD0(resetCommands, void());
     MOCK_METHOD0(executeCommands, Error());
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
-    MOCK_METHOD5(createVirtualDisplay,
-                 Error(uint32_t, uint32_t, PixelFormat*, std::optional<Display>, Display*));
+    MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
     MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
     MOCK_METHOD1(acceptDisplayChanges, Error(Display));
     MOCK_METHOD2(createLayer, Error(Display, Layer* outLayer));
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d7fdab5..cf774fd 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -979,6 +979,8 @@
 void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
     VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
+    ATRACE_CALL();
+
     // Request the android-specific presentation properties via GPDP2
     VkPhysicalDeviceProperties2 properties = {
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
@@ -994,7 +996,17 @@
     presentation_properties->pNext = nullptr;
     presentation_properties->sharedImage = VK_FALSE;
 
-    GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceProperties2) {
+        // >= 1.1 driver, supports core GPDP2 entrypoint.
+        driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    } else if (driver.GetPhysicalDeviceProperties2KHR) {
+        // Old driver, but may support presentation properties
+        // if we have the GPDP2 extension. Otherwise, no presentation
+        // properties supported.
+        driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties);
+    }
 }
 
 VkResult EnumerateDeviceExtensionProperties(