Merge "Pass dequeue timestamp along with buffer data" into main
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 649a13e..a24546a 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -10,6 +10,14 @@
     },
     {
       "name": "dumpstate_test"
+    },
+    {
+        "name": "CtsSecurityHostTestCases",
+        "options": [
+            {
+                "include-filter": "android.security.cts.SELinuxHostTest#testNoBugreportDenials"
+            }
+        ]
     }
   ],
   "postsubmit": [
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d5125f0..6576ffd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -191,6 +191,7 @@
 #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
 #define DROPBOX_DIR "/data/system/dropbox"
 #define PRINT_FLAGS "/system/bin/printflags"
+#define UWB_LOG_DIR "/data/misc/apexdata/com.android.uwb/log"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -1569,6 +1570,7 @@
     printf("== ANR Traces\n");
     printf("========================================================\n");
 
+    ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
     AddAnrTraceFiles();
 
     printf("========================================================\n");
@@ -1971,6 +1973,9 @@
     RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
                CommandOptions::WithTimeout(10).Always().DropRoot().Build());
 
+    // Dump UWB UCI logs here because apexdata requires root access
+    ds.AddDir(UWB_LOG_DIR, true);
+
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
         WaitForTask(std::move(dump_traces));
@@ -2190,6 +2195,74 @@
     ds.AddDir(LOGPERSIST_DATA_DIR, false);
 }
 
+static std::string GetTimestamp(const timespec& ts) {
+    tm tm;
+    localtime_r(&ts.tv_sec, &tm);
+
+    // Reserve enough space for the entire time string, includes the space
+    // for the '\0' to make the calculations below easier by using size for
+    // the total string size.
+    std::string str(sizeof("1970-01-01 00:00:00.123456789+0830"), '\0');
+    size_t n = strftime(str.data(), str.size(), "%F %H:%M", &tm);
+    if (n == 0) {
+        return "TIMESTAMP FAILURE";
+    }
+    int num_chars = snprintf(&str[n], str.size() - n, ":%02d.%09ld", tm.tm_sec, ts.tv_nsec);
+    if (num_chars > str.size() - n) {
+        return "TIMESTAMP FAILURE";
+    }
+    n += static_cast<size_t>(num_chars);
+    if (strftime(&str[n], str.size() - n, "%z", &tm) == 0) {
+        return "TIMESTAMP FAILURE";
+    }
+    return str;
+}
+
+static std::string GetCmdline(pid_t pid) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid),
+                                         &cmdline)) {
+        return "UNKNOWN";
+    }
+    // There are '\0' terminators between arguments, convert them to spaces.
+    // But start by skipping all trailing '\0' values.
+    size_t cur = cmdline.size() - 1;
+    while (cur != 0 && cmdline[cur] == '\0') {
+        cur--;
+    }
+    if (cur == 0) {
+        return "UNKNOWN";
+    }
+    while ((cur = cmdline.rfind('\0', cur)) != std::string::npos) {
+        cmdline[cur] = ' ';
+    }
+    return cmdline;
+}
+
+static void DumpPidHeader(int fd, pid_t pid, const timespec& ts) {
+    // For consistency, the header to this message matches the one
+    // dumped by debuggerd.
+    dprintf(fd, "\n----- pid %d at %s -----\n", pid, GetTimestamp(ts).c_str());
+    dprintf(fd, "Cmd line: %s\n", GetCmdline(pid).c_str());
+}
+
+static void DumpPidFooter(int fd, pid_t pid) {
+    // For consistency, the footer to this message matches the one
+    // dumped by debuggerd.
+    dprintf(fd, "----- end %d -----\n", pid);
+}
+
+static bool DumpBacktrace(int fd, pid_t pid, bool is_java_process) {
+    int ret = dump_backtrace_to_file_timeout(
+        pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd);
+    if (ret == -1 && is_java_process) {
+        // Tried to unwind as a java process, try a native unwind.
+        dprintf(fd, "Java unwind failed for pid %d, trying a native unwind.\n", pid);
+        ret = dump_backtrace_to_file_timeout(pid, kDebuggerdNativeBacktrace, 3, fd);
+    }
+    return ret != -1;
+}
+
 Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
     const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
     const size_t buf_size = temp_file_pattern.length() + 1;
@@ -2238,16 +2311,6 @@
             continue;
         }
 
-        // Skip cached processes.
-        if (IsCached(pid)) {
-            // For consistency, the header and footer to this message match those
-            // dumped by debuggerd in the success case.
-            dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
-            dprintf(fd, "Dump skipped for cached process.\n");
-            dprintf(fd, "---- end %d ----", pid);
-            continue;
-        }
-
         const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
         std::string exe;
         if (!android::base::Readlink(link_name, &exe)) {
@@ -2276,16 +2339,31 @@
             break;
         }
 
-        const uint64_t start = Nanotime();
-        const int ret = dump_backtrace_to_file_timeout(
-            pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, 3, fd);
+        timespec start_timespec;
+        clock_gettime(CLOCK_REALTIME, &start_timespec);
+        if (IsCached(pid)) {
+            DumpPidHeader(fd, pid, start_timespec);
+            dprintf(fd, "Process is cached, skipping backtrace due to high chance of timeout.\n");
+            DumpPidFooter(fd, pid);
+            continue;
+        }
 
-        if (ret == -1) {
-            // For consistency, the header and footer to this message match those
-            // dumped by debuggerd in the success case.
-            dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
-            dprintf(fd, "Dump failed, likely due to a timeout.\n");
-            dprintf(fd, "---- end %d ----", pid);
+        const uint64_t start = Nanotime();
+        if (!DumpBacktrace(fd, pid, is_java_process)) {
+            if (IsCached(pid)) {
+                DumpPidHeader(fd, pid, start_timespec);
+                dprintf(fd, "Backtrace failed, but process has become cached.\n");
+                DumpPidFooter(fd, pid);
+                continue;
+            }
+
+            DumpPidHeader(fd, pid, start_timespec);
+            dprintf(fd, "Backtrace gathering failed, likely due to a timeout.\n");
+            DumpPidFooter(fd, pid);
+
+            dprintf(fd, "\n[dump %s stack %d: %.3fs elapsed]\n",
+                    is_java_process ? "dalvik" : "native", pid,
+                    (float)(Nanotime() - start) / NANOS_PER_SEC);
             timeout_failures++;
             continue;
         }
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
index 88801ca..0ec5abb 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -51,7 +51,7 @@
 }
 
 template<typename Func>
-bool ParseFile(std::string_view str_file, Func parse) {
+bool ParseFile(const std::string& str_file, Func parse) {
   std::ifstream ifs(str_file);
   if (!ifs.is_open()) {
     return false;
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index ae7d8e0..2d889fd 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,7 +50,27 @@
   exit 1
 fi
 
-if pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
+# A source that infinitely emits arbitrary lines.
+# When connected to STDIN of another process, this source keeps STDIN open until
+# the consumer process closes STDIN or this script dies.
+function infinite_source {
+  while echo .; do
+    sleep 1
+  done
+}
+
+# Delegate to Pre-reboot Dexopt, a feature of ART Service.
+# ART Service decides what to do with this request:
+# - If Pre-reboot Dexopt is disabled or unsupported, the command returns
+#   non-zero. This is always the case if the current system is Android 14 or
+#   earlier.
+# - If Pre-reboot Dexopt is enabled in synchronous mode, the command blocks
+#   until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds or
+#   not. This is the default behavior if the current system is Android 15.
+# - If Pre-reboot Dexopt is enabled in asynchronous mode, the command schedules
+#   an asynchronous job and returns 0 immediately. The job will then run by the
+#   job scheduler when the device is idle and charging.
+if infinite_source | pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
   # Handled by Pre-reboot Dexopt.
   exit 0
 fi
diff --git a/include/android/input.h b/include/android/input.h
index fec56f0..ee98d7a 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1002,7 +1002,6 @@
  * Keyboard types.
  *
  * Refer to the documentation on android.view.InputDevice for more details.
- * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated.
  */
 enum {
     /** none */
diff --git a/include/binder/Common.h b/include/binder/Common.h
new file mode 120000
index 0000000..940f89d
--- /dev/null
+++ b/include/binder/Common.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Common.h
\ No newline at end of file
diff --git a/include/ftl/expected.h b/include/ftl/expected.h
index 57448dc..7e765c5 100644
--- a/include/ftl/expected.h
+++ b/include/ftl/expected.h
@@ -69,6 +69,36 @@
     exp_.value();                                                         \
   })
 
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short),
+// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E.
+// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not
+// need to propagate R because T is not relevant to the caller.
+//
+// Example usage:
+//
+//   using StringExp = ftl::Expected<std::string, std::errc>;
+//
+//   std::errc repeat(StringExp exp, std::string& out) {
+//     const std::string str = FTL_EXPECT(exp);
+//     out = str + str;
+//     return std::errc::operation_in_progress;
+//   }
+//
+//   std::string str;
+//   assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str));
+//   assert("haha"s == str);
+//   assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str));
+//   assert("haha"s == str);
+//
+#define FTL_EXPECT(expr)              \
+  ({                                  \
+    auto exp_ = (expr);               \
+    if (!exp_.has_value()) {          \
+      return std::move(exp_.error()); \
+    }                                 \
+    exp_.value();                     \
+  })
+
 namespace android::ftl {
 
 // Superset of base::expected<T, E> with monadic operations.
diff --git a/include/ftl/function.h b/include/ftl/function.h
index 3538ca4..bda5b75 100644
--- a/include/ftl/function.h
+++ b/include/ftl/function.h
@@ -123,7 +123,7 @@
 //   // Create a typedef to give a more meaningful name and bound the size.
 //   using MyFunction = ftl::Function<int(std::string_view), 2>;
 //   int* ptr = nullptr;
-//   auto f1 = MyFunction::make_function(
+//   auto f1 = MyFunction::make(
 //       [cls = &cls, ptr](std::string_view sv) {
 //           return cls->on_string(ptr, sv);
 //       });
diff --git a/include/input/Input.h b/include/input/Input.h
index 3ca9c19..ec08cdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -99,6 +99,18 @@
 
     /* Motion event is inconsistent with previously sent motion events. */
     AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
+
+    /** Private flag, not used in Java. */
+    AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+            android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION,
+
+    /** Private flag, not used in Java. */
+    AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants::
+            MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+
+    /** Mask for all private flags that are not used in Java. */
+    AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
 };
 
 /**
@@ -209,8 +221,12 @@
  * Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or
  * pointing upwards in the negative Y direction, a positive angle points towards the right, and a
  * negative angle points towards the left.
+ *
+ * If the angle represents a direction that needs to be preserved, set isDirectional to true to get
+ * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set
+ * isDirectional to false to get an output range of [-pi/2, pi/2].
  */
-float transformAngle(const ui::Transform& transform, float angleRadians);
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional);
 
 /**
  * The type of the InputEvent.
@@ -258,6 +274,16 @@
     ftl_last = VIRTUAL,
 };
 
+/**
+ * The keyboard type. This should have 1:1 correspondence with the values of anonymous enum
+ * defined in input.h
+ */
+enum class KeyboardType {
+    NONE = AINPUT_KEYBOARD_TYPE_NONE,
+    NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+    ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+};
+
 bool isStylusToolType(ToolType toolType);
 
 struct PointerProperties;
@@ -462,8 +488,6 @@
     // axes, however the window scaling will not.
     void scale(float globalScale, float windowXScale, float windowYScale);
 
-    void transform(const ui::Transform& transform);
-
     inline float getX() const {
         return getAxisValue(AMOTION_EVENT_AXIS_X);
     }
@@ -930,10 +954,12 @@
     // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
     // are used to apply these transformations for different axes.
     static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
-    static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
-                                               const PointerCoords&);
-    static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
-                                                    const PointerCoords&);
+    static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
+                                               const ui::Transform&, const PointerCoords&);
+    static void calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source,
+                                                  int32_t flags, const ui::Transform&);
+    static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags,
+                                                    const ui::Transform&, const PointerCoords&);
     // The rounding precision for transformed motion events.
     static constexpr float ROUNDING_PRECISION = 0.001f;
 
diff --git a/include/input/KeyboardClassifier.h b/include/input/KeyboardClassifier.h
new file mode 100644
index 0000000..457d474
--- /dev/null
+++ b/include/input/KeyboardClassifier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+
+#include "rust/cxx.h"
+
+namespace android {
+
+namespace input {
+namespace keyboardClassifier {
+struct KeyboardClassifier;
+}
+} // namespace input
+
+/*
+ * Keyboard classifier to classify keyboard into alphabetic and non-alphabetic keyboards
+ */
+class KeyboardClassifier {
+public:
+    KeyboardClassifier();
+    /**
+     * Get the type of keyboard that the classifier currently believes the device to be.
+     */
+    KeyboardType getKeyboardType(DeviceId deviceId);
+    void notifyKeyboardChanged(DeviceId deviceId, const InputDeviceIdentifier& identifier,
+                               uint32_t deviceClasses);
+    void processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState);
+
+private:
+    std::optional<rust::Box<android::input::keyboardClassifier::KeyboardClassifier>>
+            mRustClassifier;
+    std::unordered_map<DeviceId, KeyboardType> mKeyboardTypeMap;
+};
+
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 090e35b..cb1d114 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -28,6 +28,7 @@
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
+    cmake_snapshot_supported: true,
 
     header_libs: [
         "libbinder_headers_platform_shared",
@@ -83,6 +84,124 @@
     },
 }
 
+cc_cmake_snapshot {
+    name: "binder_sdk",
+    modules: [
+        "libbinder_sdk",
+        "libbinder_sdk_single_threaded",
+        "libbinder_ndk_sdk",
+        "binderRpcTestNoKernel",
+    ],
+    prebuilts: [
+        // to enable arm64 host support, build with musl - e.g. on aosp_cf_arm64_phone
+        "aidl",
+        "libc++",
+    ],
+    include_sources: true,
+    cflags: [
+        "-DNDEBUG",
+        "-DBINDER_ENABLE_LIBLOG_ASSERT",
+        "-DBINDER_DISABLE_NATIVE_HANDLE",
+        "-DBINDER_DISABLE_BLOB",
+        "-DBINDER_NO_LIBBASE",
+        "-DBINDER_NO_KERNEL_IPC_TESTING",
+
+        // from Soong's global.go commonGlobalCflags and noOverrideGlobalCflags
+        "-Wno-c99-designator",
+        "-Wno-missing-field-initializers",
+
+        // warnings that only pop up on gcc
+        "-Wno-unknown-pragmas", // "pragma clang"
+        "-Wno-attributes", // attributes on compound-statements
+        "-Wno-psabi", // reminders about old ABI changes
+    ],
+    cflags_ignored: [
+        // gcc requires all header constexprs to be used in all dependent compilatinon units
+        "-Wunused-const-variable",
+    ],
+    library_mapping: [
+        {
+            android_name: "libssl",
+            mapped_name: "ssl",
+            package_pregenerated: "external/boringssl",
+        },
+        {
+            android_name: "libcrypto",
+            mapped_name: "crypto",
+            package_pregenerated: "external/boringssl",
+        },
+        {
+            android_name: "libgtest",
+            mapped_name: "GTest::gtest",
+            package_system: "GTest",
+        },
+        {
+            android_name: "libgtest_main",
+            mapped_name: "GTest::gtest",
+            package_system: "GTest",
+        },
+
+        // use libbinder_sdk and friends instead of full Android's libbinder
+        {
+            android_name: "libbinder_rpc_no_kernel",
+            mapped_name: "android::libbinder_sdk",
+        },
+        {
+            android_name: "libbinder_rpc_single_threaded_no_kernel",
+            mapped_name: "android::libbinder_sdk_single_threaded",
+        },
+        {
+            android_name: "libbinder_headers",
+            mapped_name: "android::libbinder_headers_base",
+        },
+        {
+            android_name: "libbinder",
+            mapped_name: "android::libbinder_sdk",
+        },
+        {
+            android_name: "libbinder_ndk",
+            mapped_name: "android::libbinder_ndk_sdk",
+        },
+        {
+            android_name: "liblog",
+            mapped_name: "android::liblog_stub",
+        },
+
+        // explicitly included by Binder tests, but not needed outside of Android
+        {
+            android_name: "libbase",
+        },
+        {
+            android_name: "libcutils",
+        },
+        {
+            android_name: "libutils",
+        },
+
+        // disable tests that don't work outside of Android yet
+        {
+            android_name: "binder_rpc_test_service",
+        },
+        {
+            android_name: "binder_rpc_test_service_single_threaded",
+        },
+
+        // trusty mocks are artificially triggered and not needed outside of Android build
+        {
+            android_name: "libbinder_on_trusty_mock",
+        },
+        {
+            android_name: "libbinder_ndk_on_trusty_mock",
+        },
+        {
+            android_name: "binderRpcTestService_on_trusty_mock",
+        },
+        {
+            android_name: "binderRpcTest_on_trusty_mock",
+        },
+    ],
+}
+
 // These interfaces are android-specific implementation unrelated to binder
 // transport itself and should be moved to AIDL or in domain-specific libs.
 //
@@ -126,6 +245,9 @@
     header_libs: [
         "libbinder_headers_base",
     ],
+    export_header_lib_headers: [
+        "libbinder_headers_base",
+    ],
 
     cflags: [
         "-Wextra",
@@ -369,6 +491,7 @@
     double_loadable: true,
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
+    cmake_snapshot_supported: false,
 
     // libbinder does not offer a stable wire protocol.
     // if a second copy of it is installed, then it may break after security
@@ -408,16 +531,8 @@
     afdo: true,
 }
 
-cc_library_host_shared {
-    name: "libbinder_sdk",
-
-    defaults: [
-        "libbinder_common_defaults",
-    ],
-
-    shared_libs: [
-        "libutils_binder_sdk",
-    ],
+cc_defaults {
+    name: "binder_sdk_defaults",
 
     cflags: [
         "-DBINDER_ENABLE_LIBLOG_ASSERT",
@@ -429,6 +544,21 @@
     header_libs: [
         "liblog_stub",
     ],
+}
+
+cc_defaults {
+    name: "libbinder_sdk_defaults",
+
+    cmake_snapshot_supported: true,
+
+    defaults: [
+        "libbinder_common_defaults",
+        "binder_sdk_defaults",
+    ],
+
+    shared_libs: [
+        "libutils_binder_sdk",
+    ],
 
     srcs: [
         "OS_non_android_linux.cpp",
@@ -446,6 +576,19 @@
     },
 }
 
+cc_library_host_shared {
+    name: "libbinder_sdk",
+    defaults: ["libbinder_sdk_defaults"],
+}
+
+cc_library_host_shared {
+    name: "libbinder_sdk_single_threaded",
+    defaults: ["libbinder_sdk_defaults"],
+    cflags: [
+        "-DBINDER_RPC_SINGLE_THREADED",
+    ],
+}
+
 cc_library {
     name: "libbinder_rpc_no_kernel",
     vendor_available: true,
@@ -535,6 +678,7 @@
     defaults: ["libbinder_tls_shared_deps"],
     vendor_available: true,
     host_supported: true,
+    cmake_snapshot_supported: true,
 
     header_libs: [
         "libbinder_headers",
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 7a855e1..6594aa6 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -15,7 +15,6 @@
  */
 
 #define LOG_TAG "BpBinder"
-#define ATRACE_TAG ATRACE_TAG_AIDL
 //#define LOG_NDEBUG 0
 
 #include <binder/BpBinder.h>
@@ -24,15 +23,10 @@
 #include <binder/IResultReceiver.h>
 #include <binder/RpcSession.h>
 #include <binder/Stability.h>
+#include <binder/Trace.h>
 
 #include <stdio.h>
 
-#ifndef __TRUSTY__
-#include <cutils/trace.h>
-#else
-#define ATRACE_INT(...)
-#endif
-
 #include "BuildFlags.h"
 #include "file.h"
 
@@ -216,7 +210,7 @@
         sTrackingMap[trackedUid]++;
     }
     uint32_t numProxies = sBinderProxyCount.fetch_add(1, std::memory_order_relaxed);
-    ATRACE_INT("binder_proxies", numProxies);
+    binder::os::trace_int(ATRACE_TAG_AIDL, "binder_proxies", numProxies);
     uint32_t numLastWarned = sBinderProxyCountWarned.load(std::memory_order_relaxed);
     uint32_t numNextWarn = numLastWarned + kBinderProxyCountWarnInterval;
     if (numProxies >= numNextWarn) {
@@ -640,8 +634,8 @@
             }
         }
     }
-    [[maybe_unused]] uint32_t numProxies = --sBinderProxyCount;
-    ATRACE_INT("binder_proxies", numProxies);
+    uint32_t numProxies = --sBinderProxyCount;
+    binder::os::trace_int(ATRACE_TAG_AIDL, "binder_proxies", numProxies);
     if (ipc) {
         ipc->expungeHandle(binderHandle(), this);
         ipc->decWeakHandle(binderHandle());
diff --git a/libs/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
index 57e03cb..1c074e6 100644
--- a/libs/binder/BufferedTextOutput.h
+++ b/libs/binder/BufferedTextOutput.h
@@ -24,8 +24,7 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
-class BufferedTextOutput : public TextOutput
-{
+class LIBBINDER_INTERNAL_EXPORTED BufferedTextOutput : public TextOutput {
 public:
     //** Flags for constructor */
     enum {
diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h
index 262dfba..c3d03f8 100644
--- a/libs/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -20,6 +20,8 @@
 #include <sys/types.h>
 #include <string>
 
+#include <binder/Common.h>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -35,7 +37,8 @@
     size_t alignment=0, bool cArrayStyle=false,
     debugPrintFunc func = nullptr, void* cookie = nullptr);
 
-extern "C" ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
+// Used by libmemunreachable.
+extern "C" LIBBINDER_EXPORTED ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
 
 // ---------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index e4a0283..78cdaaa 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -25,7 +25,7 @@
 namespace android {
 
 /** This is not a pipe. */
-class FdTrigger {
+class LIBBINDER_INTERNAL_EXPORTED FdTrigger {
 public:
     /** Returns nullptr for error case */
     static std::unique_ptr<FdTrigger> make();
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index ef96f80..fbc8125 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1027,6 +1027,7 @@
             goto finish;
 
         case BR_FROZEN_REPLY:
+            ALOGW("Transaction failed because process frozen.");
             err = FAILED_TRANSACTION;
             goto finish;
 
@@ -1578,8 +1579,8 @@
     }
 #endif
 
-    ALOGE_IF(ee.command != BR_OK, "Binder transaction failure: %d/%d/%d",
-             ee.id, ee.command, ee.param);
+    ALOGE_IF(ee.command != BR_OK, "Binder transaction failure. id: %d, BR_*: %d, error: %d (%s)",
+             ee.id, ee.command, ee.param, strerror(-ee.param));
 }
 
 void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/,
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 0035aeb..04869a1 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -24,8 +24,9 @@
 
 namespace android::binder::os {
 
-void trace_begin(uint64_t tag, const char* name);
-void trace_end(uint64_t tag);
+LIBBINDER_EXPORTED void trace_begin(uint64_t tag, const char* name);
+LIBBINDER_EXPORTED void trace_end(uint64_t tag);
+LIBBINDER_EXPORTED void trace_int(uint64_t tag, const char* name, int32_t value);
 
 status_t setNonBlocking(borrowed_fd fd);
 
@@ -35,11 +36,13 @@
 
 std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory();
 
-ssize_t sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
-                            const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
+LIBBINDER_INTERNAL_EXPORTED ssize_t
+sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+                    const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
 
-ssize_t receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
-                                 std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
+LIBBINDER_INTERNAL_EXPORTED ssize_t
+receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+                         std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
 
 uint64_t GetThreadId();
 
diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp
index 1eace85..893ee15 100644
--- a/libs/binder/OS_android.cpp
+++ b/libs/binder/OS_android.cpp
@@ -44,6 +44,10 @@
     atrace_end(tag);
 }
 
+void trace_int(uint64_t tag, const char* name, int32_t value) {
+    atrace_int(tag, name, value);
+}
+
 } // namespace os
 
 // Legacy trace symbol. To be removed once all of downstream rebuilds.
diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp
index b525d1a..0c64eb6 100644
--- a/libs/binder/OS_non_android_linux.cpp
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -21,6 +21,8 @@
 #include <syscall.h>
 #include <cstdarg>
 
+#include <binder/Common.h>
+
 #ifdef __ANDROID__
 #error "This module is not intended for Android, just bare Linux"
 #endif
@@ -37,6 +39,8 @@
 
 void trace_end(uint64_t) {}
 
+void trace_int(uint64_t, const char*, int32_t) {}
+
 uint64_t GetThreadId() {
     return syscall(__NR_gettid);
 }
@@ -47,7 +51,8 @@
 
 } // namespace android::binder::os
 
-int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt, ...) {
+LIBBINDER_EXPORTED int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt,
+                                           ...) {
     va_list args;
     va_start(args, fmt);
     vfprintf(stderr, fmt, args);
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 1504715..5b157cc 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -82,13 +82,12 @@
          }                                                               \
     }
 
-#define RETURN_IF_ENTRY_ERASED(map, key)                                 \
-    {                                                                    \
-        size_t num_erased = (map).erase(key);                            \
-        if (num_erased) {                                                \
-            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
-            return num_erased;                                           \
-         }                                                               \
+#define RETURN_IF_ENTRY_ERASED(map, key)      \
+    {                                         \
+        size_t num_erased = (map).erase(key); \
+        if (num_erased) {                     \
+            return num_erased;                \
+        }                                     \
     }
 
 status_t PersistableBundle::writeToParcel(Parcel* parcel) const {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index fb2781b..8485ecd 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -516,22 +516,23 @@
     return mDriverName;
 }
 
-static unique_fd open_driver(const char* driver) {
+static unique_fd open_driver(const char* driver, String8* error) {
     auto fd = unique_fd(open(driver, O_RDWR | O_CLOEXEC));
     if (!fd.ok()) {
-        PLOGE("Opening '%s' failed", driver);
+        error->appendFormat("%d (%s) Opening '%s' failed", errno, strerror(errno), driver);
         return {};
     }
     int vers = 0;
     int result = ioctl(fd.get(), BINDER_VERSION, &vers);
     if (result == -1) {
-        PLOGE("Binder ioctl to obtain version failed");
+        error->appendFormat("%d (%s) Binder ioctl to obtain version failed", errno,
+                            strerror(errno));
         return {};
     }
     if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
-        ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! "
-              "ioctl() return value: %d",
-              vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
+        error->appendFormat("Binder driver protocol(%d) does not match user space protocol(%d)! "
+                            "ioctl() return value: %d",
+                            vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
         return {};
     }
     size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
@@ -565,7 +566,8 @@
         mThreadPoolStarted(false),
         mThreadPoolSeq(1),
         mCallRestriction(CallRestriction::NONE) {
-    unique_fd opened = open_driver(driver);
+    String8 error;
+    unique_fd opened = open_driver(driver, &error);
 
     if (opened.ok()) {
         // mmap the binder, providing a chunk of virtual address space to receive transactions.
@@ -580,8 +582,9 @@
     }
 
 #ifdef __ANDROID__
-    LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.",
-                        driver);
+    LOG_ALWAYS_FATAL_IF(!opened.ok(),
+                        "Binder driver '%s' could not be opened. Error: %s. Terminating.",
+                        error.c_str(), driver);
 #endif
 
     if (opened.ok()) {
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 8b84602..94013cc 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -149,8 +149,8 @@
      */
     [[nodiscard]] status_t sendObituaries(const sp<RpcSession>& session);
 
-    size_t countBinders();
-    void dump();
+    LIBBINDER_INTERNAL_EXPORTED size_t countBinders();
+    LIBBINDER_INTERNAL_EXPORTED void dump();
 
     /**
      * Called when reading or writing data to a session fails to clean up
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index eec09eb..df8a4ce 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -21,6 +21,7 @@
 #include <cstdint>
 #include <optional>
 
+#include <binder/Common.h>
 #include <log/log.h>
 #include <utils/Errors.h>
 
@@ -111,6 +112,6 @@
 //
 // Hex values are printed in order, e.g. 0xDEAD will result in 'adde' because
 // Android is little-endian.
-std::string HexString(const void* bytes, size_t len);
+LIBBINDER_INTERNAL_EXPORTED std::string HexString(const void* bytes, size_t len);
 
 }   // namespace android
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index d6fe9fa..03af1e7 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include <android-base/macros.h>
+#include <binder/Common.h>
 #include <binder/unique_fd.h>
 #include <utils/Errors.h>
 
@@ -40,7 +41,7 @@
 
 namespace android {
 
-struct CommandResult {
+struct LIBBINDER_EXPORTED CommandResult {
     std::optional<int32_t> exitCode;
     std::optional<int32_t> signal;
     std::optional<pid_t> pid;
@@ -72,7 +73,7 @@
     void operator=(const CommandResult&) = delete;
 };
 
-std::ostream& operator<<(std::ostream& os, const CommandResult& res);
+LIBBINDER_EXPORTED std::ostream& operator<<(std::ostream& os, const CommandResult& res);
 
 // Execute a command using tokens specified in @a argStringVec.
 //
@@ -96,6 +97,7 @@
 //
 // If the parent process has encountered any errors for system calls, return ExecuteError with
 // the proper errno set.
-std::optional<CommandResult> execute(std::vector<std::string> argStringVec,
-                                     const std::function<bool(const CommandResult&)>& end);
+LIBBINDER_EXPORTED std::optional<CommandResult> execute(
+        std::vector<std::string> argStringVec,
+        const std::function<bool(const CommandResult&)>& end);
 } // namespace android
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 7a65ff4..135be89 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -18,6 +18,7 @@
 
 #include <atomic>
 #include <stdint.h>
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 
 // ---------------------------------------------------------------------------
@@ -27,50 +28,47 @@
 class Stability;
 }
 
-class BBinder : public IBinder
-{
+class BBinder : public IBinder {
 public:
-                        BBinder();
+    LIBBINDER_EXPORTED BBinder();
 
-    virtual const String16& getInterfaceDescriptor() const;
-    virtual bool        isBinderAlive() const;
-    virtual status_t    pingBinder();
-    virtual status_t    dump(int fd, const Vector<String16>& args);
+    LIBBINDER_EXPORTED virtual const String16& getInterfaceDescriptor() const;
+    LIBBINDER_EXPORTED virtual bool isBinderAlive() const;
+    LIBBINDER_EXPORTED virtual status_t pingBinder();
+    LIBBINDER_EXPORTED virtual status_t dump(int fd, const Vector<String16>& args);
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    transact(   uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0) final;
+    LIBBINDER_EXPORTED virtual status_t transact(uint32_t code, const Parcel& data, Parcel* reply,
+                                                 uint32_t flags = 0) final;
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    linkToDeath(const sp<DeathRecipient>& recipient,
-                                    void* cookie = nullptr,
-                                    uint32_t flags = 0);
+    LIBBINDER_EXPORTED virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+                                                    void* cookie = nullptr, uint32_t flags = 0);
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    unlinkToDeath(  const wp<DeathRecipient>& recipient,
-                                        void* cookie = nullptr,
-                                        uint32_t flags = 0,
-                                        wp<DeathRecipient>* outRecipient = nullptr);
+    LIBBINDER_EXPORTED virtual status_t unlinkToDeath(const wp<DeathRecipient>& recipient,
+                                                      void* cookie = nullptr, uint32_t flags = 0,
+                                                      wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
-                               object_cleanup_func func) final;
-    virtual void*       findObject(const void* objectID) const final;
-    virtual void* detachObject(const void* objectID) final;
-    void withLock(const std::function<void()>& doWithLock);
-    sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
-                                   const void* makeArgs);
+    LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
+                                                  void* cleanupCookie,
+                                                  object_cleanup_func func) final;
+    LIBBINDER_EXPORTED virtual void* findObject(const void* objectID) const final;
+    LIBBINDER_EXPORTED virtual void* detachObject(const void* objectID) final;
+    LIBBINDER_EXPORTED void withLock(const std::function<void()>& doWithLock);
+    LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
+                                                      IBinder::object_make_func make,
+                                                      const void* makeArgs);
 
-    virtual BBinder*    localBinder();
+    LIBBINDER_EXPORTED virtual BBinder* localBinder();
 
-    bool                isRequestingSid();
+    LIBBINDER_EXPORTED bool isRequestingSid();
     // This must be called before the object is sent to another process. Not thread safe.
-    void                setRequestingSid(bool requestSid);
+    LIBBINDER_EXPORTED void setRequestingSid(bool requestSid);
 
-    sp<IBinder>         getExtension();
+    LIBBINDER_EXPORTED sp<IBinder> getExtension();
     // This must be called before the object is sent to another process. Not thread safe.
-    void                setExtension(const sp<IBinder>& extension);
+    LIBBINDER_EXPORTED void setExtension(const sp<IBinder>& extension);
 
     // This must be called before the object is sent to another process. Not thread safe.
     //
@@ -84,35 +82,33 @@
     // Appropriate values are:
     // SCHED_NORMAL: -20 <= priority <= 19
     // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99
-    void                setMinSchedulerPolicy(int policy, int priority);
-    int                 getMinSchedulerPolicy();
-    int                 getMinSchedulerPriority();
+    LIBBINDER_EXPORTED void setMinSchedulerPolicy(int policy, int priority);
+    LIBBINDER_EXPORTED int getMinSchedulerPolicy();
+    LIBBINDER_EXPORTED int getMinSchedulerPriority();
 
     // Whether realtime scheduling policies are inherited.
-    bool                isInheritRt();
+    LIBBINDER_EXPORTED bool isInheritRt();
     // This must be called before the object is sent to another process. Not thread safe.
-    void                setInheritRt(bool inheritRt);
+    LIBBINDER_EXPORTED void setInheritRt(bool inheritRt);
 
-    pid_t               getDebugPid();
+    LIBBINDER_EXPORTED pid_t getDebugPid();
 
     // Whether this binder has been sent to another process.
-    bool wasParceled();
+    LIBBINDER_EXPORTED bool wasParceled();
     // Consider this binder as parceled (setup/init-related calls should no
     // longer by called. This is automatically set by when this binder is sent
     // to another process.
-    void setParceled();
+    LIBBINDER_EXPORTED void setParceled();
 
-    [[nodiscard]] status_t setRpcClientDebug(binder::unique_fd clientFd,
-                                             const sp<IBinder>& keepAliveBinder);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setRpcClientDebug(binder::unique_fd clientFd,
+                                                                const sp<IBinder>& keepAliveBinder);
 
 protected:
-    virtual             ~BBinder();
+    LIBBINDER_EXPORTED virtual ~BBinder();
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
+    LIBBINDER_EXPORTED virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                                   uint32_t flags = 0);
 
 private:
                         BBinder(const BBinder& o);
@@ -142,17 +138,18 @@
 
 // ---------------------------------------------------------------------------
 
-class BpRefBase : public virtual RefBase
-{
+class BpRefBase : public virtual RefBase {
 protected:
-    explicit                BpRefBase(const sp<IBinder>& o);
-    virtual                 ~BpRefBase();
-    virtual void            onFirstRef();
-    virtual void            onLastStrongRef(const void* id);
-    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
+    LIBBINDER_EXPORTED explicit BpRefBase(const sp<IBinder>& o);
+    LIBBINDER_EXPORTED virtual ~BpRefBase();
+    LIBBINDER_EXPORTED virtual void onFirstRef();
+    LIBBINDER_EXPORTED virtual void onLastStrongRef(const void* id);
+    LIBBINDER_EXPORTED virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
 
-    inline IBinder* remote() const { return mRemote; }
-    inline sp<IBinder> remoteStrong() const { return sp<IBinder>::fromExisting(mRemote); }
+    LIBBINDER_EXPORTED inline IBinder* remote() const { return mRemote; }
+    LIBBINDER_EXPORTED inline sp<IBinder> remoteStrong() const {
+        return sp<IBinder>::fromExisting(mRemote);
+    }
 
 private:
                             BpRefBase(const BpRefBase& o);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 9f03907..8ac30ba 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 #include <binder/RpcThreads.h>
 #include <binder/unique_fd.h>
@@ -38,67 +39,64 @@
 using binder_proxy_limit_callback = std::function<void(int)>;
 using binder_proxy_warning_callback = std::function<void(int)>;
 
-class BpBinder : public IBinder
-{
+class BpBinder : public IBinder {
 public:
     /**
      * Return value:
      * true - this is associated with a socket RpcSession
      * false - (usual) binder over e.g. /dev/binder
      */
-    bool isRpcBinder() const;
+    LIBBINDER_EXPORTED bool isRpcBinder() const;
 
-    virtual const String16&    getInterfaceDescriptor() const;
-    virtual bool        isBinderAlive() const;
-    virtual status_t    pingBinder();
-    virtual status_t    dump(int fd, const Vector<String16>& args);
+    LIBBINDER_EXPORTED virtual const String16& getInterfaceDescriptor() const;
+    LIBBINDER_EXPORTED virtual bool isBinderAlive() const;
+    LIBBINDER_EXPORTED virtual status_t pingBinder();
+    LIBBINDER_EXPORTED virtual status_t dump(int fd, const Vector<String16>& args);
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    transact(   uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0) final;
+    LIBBINDER_EXPORTED virtual status_t transact(uint32_t code, const Parcel& data, Parcel* reply,
+                                                 uint32_t flags = 0) final;
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    linkToDeath(const sp<DeathRecipient>& recipient,
-                                    void* cookie = nullptr,
-                                    uint32_t flags = 0);
+    LIBBINDER_EXPORTED virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+                                                    void* cookie = nullptr, uint32_t flags = 0);
 
     // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    unlinkToDeath(  const wp<DeathRecipient>& recipient,
-                                        void* cookie = nullptr,
-                                        uint32_t flags = 0,
-                                        wp<DeathRecipient>* outRecipient = nullptr);
+    LIBBINDER_EXPORTED virtual status_t unlinkToDeath(const wp<DeathRecipient>& recipient,
+                                                      void* cookie = nullptr, uint32_t flags = 0,
+                                                      wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
-                               object_cleanup_func func) final;
-    virtual void*       findObject(const void* objectID) const final;
-    virtual void* detachObject(const void* objectID) final;
-    void withLock(const std::function<void()>& doWithLock);
-    sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
-                                   const void* makeArgs);
+    LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
+                                                  void* cleanupCookie,
+                                                  object_cleanup_func func) final;
+    LIBBINDER_EXPORTED virtual void* findObject(const void* objectID) const final;
+    LIBBINDER_EXPORTED virtual void* detachObject(const void* objectID) final;
+    LIBBINDER_EXPORTED void withLock(const std::function<void()>& doWithLock);
+    LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
+                                                      IBinder::object_make_func make,
+                                                      const void* makeArgs);
 
-    virtual BpBinder*   remoteBinder();
+    LIBBINDER_EXPORTED virtual BpBinder* remoteBinder();
 
-            void        sendObituary();
+    LIBBINDER_EXPORTED void sendObituary();
 
-    static uint32_t     getBinderProxyCount(uint32_t uid);
-    static void         getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts);
-    static void         enableCountByUid();
-    static void         disableCountByUid();
-    static void         setCountByUidEnabled(bool enable);
-    static void         setBinderProxyCountEventCallback(binder_proxy_limit_callback cbl,
-                                                         binder_proxy_warning_callback cbw);
-    static void         setBinderProxyCountWatermarks(int high, int low, int warning);
-    static uint32_t     getBinderProxyCount();
+    LIBBINDER_EXPORTED static uint32_t getBinderProxyCount(uint32_t uid);
+    LIBBINDER_EXPORTED static void getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts);
+    LIBBINDER_EXPORTED static void enableCountByUid();
+    LIBBINDER_EXPORTED static void disableCountByUid();
+    LIBBINDER_EXPORTED static void setCountByUidEnabled(bool enable);
+    LIBBINDER_EXPORTED static void setBinderProxyCountEventCallback(
+            binder_proxy_limit_callback cbl, binder_proxy_warning_callback cbw);
+    LIBBINDER_EXPORTED static void setBinderProxyCountWatermarks(int high, int low, int warning);
+    LIBBINDER_EXPORTED static uint32_t getBinderProxyCount();
 
-    std::optional<int32_t> getDebugBinderHandle() const;
+    LIBBINDER_EXPORTED std::optional<int32_t> getDebugBinderHandle() const;
 
     // Start recording transactions to the unique_fd.
     // See RecordedTransaction.h for more details.
-    status_t startRecordingBinder(const binder::unique_fd& fd);
+    LIBBINDER_EXPORTED status_t startRecordingBinder(const binder::unique_fd& fd);
     // Stop the current recording.
-    status_t stopRecordingBinder();
+    LIBBINDER_EXPORTED status_t stopRecordingBinder();
 
     class ObjectManager {
     public:
@@ -150,7 +148,9 @@
 
         const BpBinder* mBinder;
     };
-    const PrivateAccessor getPrivateAccessor() const { return PrivateAccessor(this); }
+    LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const {
+        return PrivateAccessor(this);
+    }
 
 private:
     friend PrivateAccessor;
diff --git a/libs/binder/include/binder/Common.h b/libs/binder/include/binder/Common.h
new file mode 100644
index 0000000..ed10154
--- /dev/null
+++ b/libs/binder/include/binder/Common.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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
+
+// libbinder is built with symbol hidden by default. To add a new symbol to the
+// ABI, you must annotate it with this LIBBINDER_EXPORTED macro. When not
+// building libbinder (e.g. when another binary includes a libbinder header),
+// this macro is a no-op.
+//
+// Examples:
+//
+//     // Export a function.
+//     LIBBINDER_EXPORTED void someFunction();
+//
+//     // Export a subset of the symbols for a class.
+//     class SomeClassA {
+//       public:
+//         LIBBINDER_EXPORTED SomeClassA();
+//
+//         LIBBINDER_EXPORTED SomeMethod();
+//     }
+//
+//     // Export all the symbols for a class, even private symbols.
+//     class LIBBINDER_EXPORTED SomeClassB {};
+//
+// For a more detailed explanation of this strategy, see
+// https://www.gnu.org/software/gnulib/manual/html_node/Exported-Symbols-of-Shared-Libraries.html
+#if BUILDING_LIBBINDER
+#define LIBBINDER_EXPORTED __attribute__((__visibility__("default")))
+#else
+#define LIBBINDER_EXPORTED
+#endif
+
+// For stuff that is exported but probably shouldn't be. It behaves the exact
+// same way as LIBBINDER_EXPORTED, only exists to help track what we want
+// eventually remove.
+//
+// Needed, at least in part, because the test binaries are using internal
+// headers and accessing these symbols directly.
+#define LIBBINDER_INTERNAL_EXPORTED LIBBINDER_EXPORTED
diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h
index ad5a6a3..7aaa7a0 100644
--- a/libs/binder/include/binder/Delegate.h
+++ b/libs/binder/include/binder/Delegate.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 
 #if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index dad9a17..17248ce 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/unique_fd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -48,8 +49,7 @@
  * (method calls, property get and set) is down through a low-level
  * protocol implemented on top of the transact() API.
  */
-class [[clang::lto_visibility_public]] IBinder : public virtual RefBase
-{
+class [[clang::lto_visibility_public]] LIBBINDER_EXPORTED IBinder : public virtual RefBase {
 public:
     enum {
         FIRST_CALL_TRANSACTION = 0x00000001,
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index ac845bc..30e005c 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <binder/Binder.h>
+#include <binder/Common.h>
 
 #include <assert.h>
 
@@ -24,8 +25,7 @@
 
 // ----------------------------------------------------------------------
 
-class IInterface : public virtual RefBase
-{
+class LIBBINDER_EXPORTED IInterface : public virtual RefBase {
 public:
             IInterface();
             static sp<IBinder>  asBinder(const IInterface*);
@@ -66,9 +66,8 @@
 
 // ----------------------------------------------------------------------
 
-template<typename INTERFACE>
-class BnInterface : public INTERFACE, public BBinder
-{
+template <typename INTERFACE>
+class LIBBINDER_EXPORTED BnInterface : public INTERFACE, public BBinder {
 public:
     virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
     virtual const String16&     getInterfaceDescriptor() const;
@@ -80,9 +79,8 @@
 
 // ----------------------------------------------------------------------
 
-template<typename INTERFACE>
-class BpInterface : public INTERFACE, public BpRefBase
-{
+template <typename INTERFACE>
+class LIBBINDER_EXPORTED BpInterface : public INTERFACE, public BpRefBase {
 public:
     explicit                    BpInterface(const sp<IBinder>& remote);
     typedef INTERFACE BaseInterface;
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index d8b7ec1..12c5c61 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -22,14 +22,14 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 
 namespace android {
 
 // ----------------------------------------------------------------------------
 
-class IMemoryHeap : public IInterface
-{
+class LIBBINDER_EXPORTED IMemoryHeap : public IInterface {
 public:
     DECLARE_META_INTERFACE(MemoryHeap)
 
@@ -50,8 +50,7 @@
     size_t  virtualSize() const { return getSize(); }
 };
 
-class BnMemoryHeap : public BnInterface<IMemoryHeap>
-{
+class LIBBINDER_EXPORTED BnMemoryHeap : public BnInterface<IMemoryHeap> {
 public:
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t onTransact(
@@ -67,8 +66,7 @@
 
 // ----------------------------------------------------------------------------
 
-class IMemory : public IInterface
-{
+class LIBBINDER_EXPORTED IMemory : public IInterface {
 public:
     DECLARE_META_INTERFACE(Memory)
 
@@ -105,8 +103,7 @@
     void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
 };
 
-class BnMemory : public BnInterface<IMemory>
-{
+class LIBBINDER_EXPORTED BnMemory : public BnInterface<IMemory> {
 public:
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t onTransact(
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index dc5b1a1..09ab442 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -16,9 +16,10 @@
 
 #pragma once
 
-#include <utils/Errors.h>
+#include <binder/Common.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
+#include <utils/Errors.h>
 #include <utils/Vector.h>
 
 #if defined(_WIN32)
@@ -32,13 +33,12 @@
  * Kernel binder thread state. All operations here refer to kernel binder. This
  * object is allocated per-thread.
  */
-class IPCThreadState
-{
+class IPCThreadState {
 public:
     using CallRestriction = ProcessState::CallRestriction;
 
-    static  IPCThreadState*     self();
-    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
+    LIBBINDER_EXPORTED static IPCThreadState* self();
+    LIBBINDER_EXPORTED static IPCThreadState* selfOrNull(); // self(), but won't instantiate
 
     // Freeze or unfreeze the binder interface to a specific process. When freezing, this method
     // will block up to timeout_ms to process pending transactions directed to pid. Unfreeze
@@ -52,182 +52,175 @@
     // binder transactions to be processed.
     //
     // returns: 0 in case of success, a value < 0 in case of error
-    static  status_t            freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
+    LIBBINDER_EXPORTED static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
 
     // Provide information about the state of a frozen process
-    static  status_t            getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
-                                                    uint32_t *async_received);
+    LIBBINDER_EXPORTED static status_t getProcessFreezeInfo(pid_t pid, uint32_t* sync_received,
+                                                            uint32_t* async_received);
 
-            status_t            clearLastError();
+    LIBBINDER_EXPORTED status_t clearLastError();
 
-            /**
-             * Returns the PID of the process which has made the current binder
-             * call. If not in a binder call, this will return getpid.
-             *
-             * Warning: oneway transactions do not receive PID. Even if you expect
-             * a transaction to be synchronous, a misbehaving client could send it
-             * as an asynchronous call and result in a 0 PID here. Additionally, if
-             * there is a race and the calling process dies, the PID may still be
-             * 0 for a synchronous call.
-             */
-            [[nodiscard]] pid_t getCallingPid() const;
+    /**
+     * Returns the PID of the process which has made the current binder
+     * call. If not in a binder call, this will return getpid.
+     *
+     * Warning: oneway transactions do not receive PID. Even if you expect
+     * a transaction to be synchronous, a misbehaving client could send it
+     * as an asynchronous call and result in a 0 PID here. Additionally, if
+     * there is a race and the calling process dies, the PID may still be
+     * 0 for a synchronous call.
+     */
+    [[nodiscard]] LIBBINDER_EXPORTED pid_t getCallingPid() const;
 
-            /**
-             * Returns the SELinux security identifier of the process which has
-             * made the current binder call. If not in a binder call this will
-             * return nullptr. If this isn't requested with
-             * Binder::setRequestingSid, it will also return nullptr.
-             *
-             * This can't be restored once it's cleared, and it does not return the
-             * context of the current process when not in a binder call.
-             */
-            [[nodiscard]] const char* getCallingSid() const;
+    /**
+     * Returns the SELinux security identifier of the process which has
+     * made the current binder call. If not in a binder call this will
+     * return nullptr. If this isn't requested with
+     * Binder::setRequestingSid, it will also return nullptr.
+     *
+     * This can't be restored once it's cleared, and it does not return the
+     * context of the current process when not in a binder call.
+     */
+    [[nodiscard]] LIBBINDER_EXPORTED const char* getCallingSid() const;
 
-            /**
-             * Returns the UID of the process which has made the current binder
-             * call. If not in a binder call, this will return 0.
-             */
-            [[nodiscard]] uid_t getCallingUid() const;
+    /**
+     * Returns the UID of the process which has made the current binder
+     * call. If not in a binder call, this will return 0.
+     */
+    [[nodiscard]] LIBBINDER_EXPORTED uid_t getCallingUid() const;
 
-            /**
-             * Make it an abort to rely on getCalling* for a section of
-             * execution.
-             *
-             * Usage:
-             *     IPCThreadState::SpGuard guard {
-             *        .address = __builtin_frame_address(0),
-             *        .context = "...",
-             *     };
-             *     const auto* orig = pushGetCallingSpGuard(&guard);
-             *     {
-             *         // will abort if you call getCalling*, unless you are
-             *         // serving a nested binder transaction
-             *     }
-             *     restoreCallingSpGuard(orig);
-             */
-            struct SpGuard {
-                const void* address;
-                const char* context;
-            };
-            const SpGuard* pushGetCallingSpGuard(const SpGuard* guard);
-            void restoreGetCallingSpGuard(const SpGuard* guard);
-            /**
-             * Used internally by getCalling*. Can also be used to assert that
-             * you are in a binder context (getCalling* is valid). This is
-             * intentionally not exposed as a boolean API since code should be
-             * written to know its environment.
-             */
-            void checkContextIsBinderForUse(const char* use) const;
+    /**
+     * Make it an abort to rely on getCalling* for a section of
+     * execution.
+     *
+     * Usage:
+     *     IPCThreadState::SpGuard guard {
+     *        .address = __builtin_frame_address(0),
+     *        .context = "...",
+     *     };
+     *     const auto* orig = pushGetCallingSpGuard(&guard);
+     *     {
+     *         // will abort if you call getCalling*, unless you are
+     *         // serving a nested binder transaction
+     *     }
+     *     restoreCallingSpGuard(orig);
+     */
+    struct SpGuard {
+        const void* address;
+        const char* context;
+    };
+    LIBBINDER_EXPORTED const SpGuard* pushGetCallingSpGuard(const SpGuard* guard);
+    LIBBINDER_EXPORTED void restoreGetCallingSpGuard(const SpGuard* guard);
+    /**
+     * Used internally by getCalling*. Can also be used to assert that
+     * you are in a binder context (getCalling* is valid). This is
+     * intentionally not exposed as a boolean API since code should be
+     * written to know its environment.
+     */
+    LIBBINDER_EXPORTED void checkContextIsBinderForUse(const char* use) const;
 
-            void                setStrictModePolicy(int32_t policy);
-            int32_t             getStrictModePolicy() const;
+    LIBBINDER_EXPORTED void setStrictModePolicy(int32_t policy);
+    LIBBINDER_EXPORTED int32_t getStrictModePolicy() const;
 
-            // See Binder#setCallingWorkSourceUid in Binder.java.
-            int64_t             setCallingWorkSourceUid(uid_t uid);
-            // Internal only. Use setCallingWorkSourceUid(uid) instead.
-            int64_t             setCallingWorkSourceUidWithoutPropagation(uid_t uid);
-            // See Binder#getCallingWorkSourceUid in Binder.java.
-            uid_t               getCallingWorkSourceUid() const;
-            // See Binder#clearCallingWorkSource in Binder.java.
-            int64_t             clearCallingWorkSource();
-            // See Binder#restoreCallingWorkSource in Binder.java.
-            void                restoreCallingWorkSource(int64_t token);
-            void                clearPropagateWorkSource();
-            bool                shouldPropagateWorkSource() const;
+    // See Binder#setCallingWorkSourceUid in Binder.java.
+    LIBBINDER_EXPORTED int64_t setCallingWorkSourceUid(uid_t uid);
+    // Internal only. Use setCallingWorkSourceUid(uid) instead.
+    LIBBINDER_EXPORTED int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid);
+    // See Binder#getCallingWorkSourceUid in Binder.java.
+    LIBBINDER_EXPORTED uid_t getCallingWorkSourceUid() const;
+    // See Binder#clearCallingWorkSource in Binder.java.
+    LIBBINDER_EXPORTED int64_t clearCallingWorkSource();
+    // See Binder#restoreCallingWorkSource in Binder.java.
+    LIBBINDER_EXPORTED void restoreCallingWorkSource(int64_t token);
+    LIBBINDER_EXPORTED void clearPropagateWorkSource();
+    LIBBINDER_EXPORTED bool shouldPropagateWorkSource() const;
 
-            void                setLastTransactionBinderFlags(int32_t flags);
-            int32_t             getLastTransactionBinderFlags() const;
+    LIBBINDER_EXPORTED void setLastTransactionBinderFlags(int32_t flags);
+    LIBBINDER_EXPORTED int32_t getLastTransactionBinderFlags() const;
 
-            void                setCallRestriction(CallRestriction restriction);
-            CallRestriction     getCallRestriction() const;
+    LIBBINDER_EXPORTED void setCallRestriction(CallRestriction restriction);
+    LIBBINDER_EXPORTED CallRestriction getCallRestriction() const;
 
-            int64_t             clearCallingIdentity();
-            // Restores PID/UID (not SID)
-            void                restoreCallingIdentity(int64_t token);
-            bool hasExplicitIdentity();
+    LIBBINDER_EXPORTED int64_t clearCallingIdentity();
+    // Restores PID/UID (not SID)
+    LIBBINDER_EXPORTED void restoreCallingIdentity(int64_t token);
+    LIBBINDER_EXPORTED bool hasExplicitIdentity();
 
-            // For main functions - dangerous for libraries to use
-            status_t            setupPolling(int* fd);
-            status_t            handlePolledCommands();
-            void                flushCommands();
-            bool                flushIfNeeded();
+    // For main functions - dangerous for libraries to use
+    LIBBINDER_EXPORTED status_t setupPolling(int* fd);
+    LIBBINDER_EXPORTED status_t handlePolledCommands();
+    LIBBINDER_EXPORTED void flushCommands();
+    LIBBINDER_EXPORTED bool flushIfNeeded();
 
-            // Adds the current thread into the binder threadpool.
-            //
-            // This is in addition to any threads which are started
-            // with startThreadPool. Libraries should not call this
-            // function, as they may be loaded into processes which
-            // try to configure the threadpool differently.
-            void                joinThreadPool(bool isMain = true);
-            
-            // Stop the local process.
-            void                stopProcess(bool immediate = true);
-            
-            status_t            transact(int32_t handle,
-                                         uint32_t code, const Parcel& data,
+    // Adds the current thread into the binder threadpool.
+    //
+    // This is in addition to any threads which are started
+    // with startThreadPool. Libraries should not call this
+    // function, as they may be loaded into processes which
+    // try to configure the threadpool differently.
+    LIBBINDER_EXPORTED void joinThreadPool(bool isMain = true);
+
+    // Stop the local process.
+    LIBBINDER_EXPORTED void stopProcess(bool immediate = true);
+
+    LIBBINDER_EXPORTED status_t transact(int32_t handle, uint32_t code, const Parcel& data,
                                          Parcel* reply, uint32_t flags);
 
-            void                incStrongHandle(int32_t handle, BpBinder *proxy);
-            void                decStrongHandle(int32_t handle);
-            void                incWeakHandle(int32_t handle, BpBinder *proxy);
-            void                decWeakHandle(int32_t handle);
-            status_t            attemptIncStrongHandle(int32_t handle);
-    static  void                expungeHandle(int32_t handle, IBinder* binder);
-            status_t            requestDeathNotification(   int32_t handle,
-                                                            BpBinder* proxy); 
-            status_t            clearDeathNotification( int32_t handle,
-                                                        BpBinder* proxy); 
+    LIBBINDER_EXPORTED void incStrongHandle(int32_t handle, BpBinder* proxy);
+    LIBBINDER_EXPORTED void decStrongHandle(int32_t handle);
+    LIBBINDER_EXPORTED void incWeakHandle(int32_t handle, BpBinder* proxy);
+    LIBBINDER_EXPORTED void decWeakHandle(int32_t handle);
+    LIBBINDER_EXPORTED status_t attemptIncStrongHandle(int32_t handle);
+    LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder);
+    LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy);
+    LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy);
 
-    static  void                shutdown();
+    LIBBINDER_EXPORTED static void shutdown();
 
     // Call this to disable switching threads to background scheduling when
     // receiving incoming IPC calls.  This is specifically here for the
     // Android system process, since it expects to have background apps calling
     // in to it but doesn't want to acquire locks in its services while in
     // the background.
-    static  void                disableBackgroundScheduling(bool disable);
-            bool                backgroundSchedulingDisabled();
+    LIBBINDER_EXPORTED static void disableBackgroundScheduling(bool disable);
+    LIBBINDER_EXPORTED bool backgroundSchedulingDisabled();
 
-            // Call blocks until the number of executing binder threads is less than
-            // the maximum number of binder threads threads allowed for this process.
-            void                blockUntilThreadAvailable();
+    // Call blocks until the number of executing binder threads is less than
+    // the maximum number of binder threads threads allowed for this process.
+    LIBBINDER_EXPORTED void blockUntilThreadAvailable();
 
-            // Service manager registration
-            void                setTheContextObject(const sp<BBinder>& obj);
+    // Service manager registration
+    LIBBINDER_EXPORTED void setTheContextObject(const sp<BBinder>& obj);
 
-            // WARNING: DO NOT USE THIS API
-            //
-            // Returns a pointer to the stack from the last time a transaction
-            // was initiated by the kernel. Used to compare when making nested
-            // calls between multiple different transports.
-            const void*         getServingStackPointer() const;
+    // WARNING: DO NOT USE THIS API
+    //
+    // Returns a pointer to the stack from the last time a transaction
+    // was initiated by the kernel. Used to compare when making nested
+    // calls between multiple different transports.
+    LIBBINDER_EXPORTED const void* getServingStackPointer() const;
 
-            // The work source represents the UID of the process we should attribute the transaction
-            // to. We use -1 to specify that the work source was not set using #setWorkSource.
-            //
-            // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
-            // side.
-            static const int32_t kUnsetWorkSource = -1;
+    // The work source represents the UID of the process we should attribute the transaction
+    // to. We use -1 to specify that the work source was not set using #setWorkSource.
+    //
+    // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+    // side.
+    LIBBINDER_EXPORTED static const int32_t kUnsetWorkSource = -1;
+
 private:
-                                IPCThreadState();
-                                ~IPCThreadState();
+    IPCThreadState();
+    ~IPCThreadState();
 
-            status_t            sendReply(const Parcel& reply, uint32_t flags);
-            status_t            waitForResponse(Parcel *reply,
-                                                status_t *acquireResult=nullptr);
-            status_t            talkWithDriver(bool doReceive=true);
-            status_t            writeTransactionData(int32_t cmd,
-                                                     uint32_t binderFlags,
-                                                     int32_t handle,
-                                                     uint32_t code,
-                                                     const Parcel& data,
-                                                     status_t* statusBuffer);
-            status_t            getAndExecuteCommand();
-            status_t            executeCommand(int32_t command);
-            void                processPendingDerefs();
-            void                processPostWriteDerefs();
+    status_t sendReply(const Parcel& reply, uint32_t flags);
+    status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
+    status_t talkWithDriver(bool doReceive = true);
+    status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code,
+                                  const Parcel& data, status_t* statusBuffer);
+    status_t getAndExecuteCommand();
+    status_t executeCommand(int32_t command);
+    void processPendingDerefs();
+    void processPostWriteDerefs();
 
-            void                clearCaller();
+    void clearCaller();
 
     static  void                threadDestructor(void *st);
     static void freeBuffer(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
index a4f93d9..2bf9e71 100644
--- a/libs/binder/include/binder/IPermissionController.h
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -18,6 +18,7 @@
 
 #ifndef __ANDROID_VNDK__
 
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 #include <stdlib.h>
 
@@ -25,8 +26,7 @@
 
 // ----------------------------------------------------------------------
 
-class IPermissionController : public IInterface
-{
+class LIBBINDER_EXPORTED IPermissionController : public IInterface {
 public:
     DECLARE_META_INTERFACE(PermissionController)
 
@@ -51,8 +51,7 @@
 
 // ----------------------------------------------------------------------
 
-class BnPermissionController : public BnInterface<IPermissionController>
-{
+class LIBBINDER_EXPORTED BnPermissionController : public BnInterface<IPermissionController> {
 public:
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t    onTransact( uint32_t code,
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
index 5434445..b72cf11 100644
--- a/libs/binder/include/binder/IResultReceiver.h
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 
 namespace android {
 
 // ----------------------------------------------------------------------
 
-class IResultReceiver : public IInterface
-{
+class LIBBINDER_EXPORTED IResultReceiver : public IInterface {
 public:
     DECLARE_META_INTERFACE(ResultReceiver)
 
@@ -36,8 +36,7 @@
 
 // ----------------------------------------------------------------------
 
-class BnResultReceiver : public BnInterface<IResultReceiver>
-{
+class LIBBINDER_EXPORTED BnResultReceiver : public BnInterface<IResultReceiver> {
 public:
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t    onTransact( uint32_t code,
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 486bdfb..5fb7307 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -15,6 +15,7 @@
  */
 
 #pragma once
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 #include <utils/Vector.h>
 #include <utils/String16.h>
@@ -29,8 +30,7 @@
  *
  * IInterface is only for legacy ABI compatibility
  */
-class IServiceManager : public IInterface
-{
+class LIBBINDER_EXPORTED IServiceManager : public IInterface {
 public:
     // for ABI compatibility
     virtual const String16& getInterfaceDescriptor() const;
@@ -149,7 +149,7 @@
     virtual std::vector<ServiceDebugInfo> getServiceDebugInfo() = 0;
 };
 
-sp<IServiceManager> defaultServiceManager();
+LIBBINDER_EXPORTED sp<IServiceManager> defaultServiceManager();
 
 /**
  * Directly set the default service manager. Only used for testing.
@@ -157,7 +157,7 @@
  * *before* any call to defaultServiceManager(); if the latter is
  * called first, setDefaultServiceManager() will abort.
  */
-void setDefaultServiceManager(const sp<IServiceManager>& sm);
+LIBBINDER_EXPORTED void setDefaultServiceManager(const sp<IServiceManager>& sm);
 
 template<typename INTERFACE>
 sp<INTERFACE> waitForService(const String16& name) {
@@ -207,13 +207,14 @@
     return NAME_NOT_FOUND;
 }
 
-void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag);
+LIBBINDER_EXPORTED void* openDeclaredPassthroughHal(const String16& interface,
+                                                    const String16& instance, int flag);
 
-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 logPermissionFailure = true);
+LIBBINDER_EXPORTED bool checkCallingPermission(const String16& permission);
+LIBBINDER_EXPORTED bool checkCallingPermission(const String16& permission, int32_t* outPid,
+                                               int32_t* outUid);
+LIBBINDER_EXPORTED 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.
@@ -233,7 +234,7 @@
 struct RpcDelegateServiceManagerOptions {
     std::optional<size_t> maxOutgoingConnections;
 };
-sp<IServiceManager> createRpcDelegateServiceManager(
+LIBBINDER_EXPORTED sp<IServiceManager> createRpcDelegateServiceManager(
         const RpcDelegateServiceManagerOptions& options);
 #endif
 
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
index 6d3fe4a..4324afc 100644
--- a/libs/binder/include/binder/IShellCallback.h
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 
 namespace android {
 
 // ----------------------------------------------------------------------
 
-class IShellCallback : public IInterface
-{
+class LIBBINDER_EXPORTED IShellCallback : public IInterface {
 public:
     DECLARE_META_INTERFACE(ShellCallback)
 
@@ -37,8 +37,7 @@
 
 // ----------------------------------------------------------------------
 
-class BnShellCallback : public BnInterface<IShellCallback>
-{
+class LIBBINDER_EXPORTED BnShellCallback : public BnInterface<IShellCallback> {
 public:
     // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t    onTransact( uint32_t code,
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index bda3d19..3436b11 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -18,6 +18,7 @@
 
 #include <functional>
 
+#include <binder/Common.h>
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
 #include <utils/StrongPointer.h>
@@ -42,70 +43,71 @@
  * For more information on init .rc configuration, see system/core/init/README.md
  **/
 class LazyServiceRegistrar {
-   public:
-     static LazyServiceRegistrar& getInstance();
-     status_t registerService(const sp<IBinder>& service,
-                              const std::string& name = "default",
-                              bool allowIsolated = false,
-                              int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
-     /**
-      * Force the service to persist, even when it has 0 clients.
-      * If setting this flag from the server side, make sure to do so before calling
-      * registerService, or there may be a race with the default dynamic shutdown.
-      *
-      * This should only be used if it is every eventually set to false. If a
-      * service needs to persist but doesn't need to dynamically shut down,
-      * prefer to control it with another mechanism such as ctl.start.
-      */
-     void forcePersist(bool persist);
+public:
+    LIBBINDER_EXPORTED static LazyServiceRegistrar& getInstance();
+    LIBBINDER_EXPORTED status_t
+    registerService(const sp<IBinder>& service, const std::string& name = "default",
+                    bool allowIsolated = false,
+                    int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+    /**
+     * Force the service to persist, even when it has 0 clients.
+     * If setting this flag from the server side, make sure to do so before calling
+     * registerService, or there may be a race with the default dynamic shutdown.
+     *
+     * This should only be used if it is every eventually set to false. If a
+     * service needs to persist but doesn't need to dynamically shut down,
+     * prefer to control it with another mechanism such as ctl.start.
+     */
+    LIBBINDER_EXPORTED void forcePersist(bool persist);
 
-     /**
-      * Set a callback that is invoked when the active service count (i.e. services with clients)
-      * registered with this process drops to zero (or becomes nonzero).
-      * The callback takes a boolean argument, which is 'true' if there is
-      * at least one service with clients.
-      *
-      * Callback return value:
-      * - false: Default behavior for lazy services (shut down the process if there
-      *          are no clients).
-      * - true:  Don't shut down the process even if there are no clients.
-      *
-      * This callback gives a chance to:
-      * 1 - Perform some additional operations before exiting;
-      * 2 - Prevent the process from exiting by returning "true" from the
-      *     callback.
-      *
-      * This method should be called before 'registerService' to avoid races.
-      */
-     void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
+    /**
+     * Set a callback that is invoked when the active service count (i.e. services with clients)
+     * registered with this process drops to zero (or becomes nonzero).
+     * The callback takes a boolean argument, which is 'true' if there is
+     * at least one service with clients.
+     *
+     * Callback return value:
+     * - false: Default behavior for lazy services (shut down the process if there
+     *          are no clients).
+     * - true:  Don't shut down the process even if there are no clients.
+     *
+     * This callback gives a chance to:
+     * 1 - Perform some additional operations before exiting;
+     * 2 - Prevent the process from exiting by returning "true" from the
+     *     callback.
+     *
+     * This method should be called before 'registerService' to avoid races.
+     */
+    LIBBINDER_EXPORTED void setActiveServicesCallback(
+            const std::function<bool(bool)>& activeServicesCallback);
 
-     /**
-      * Try to unregister all services previously registered with 'registerService'.
-      * Returns 'true' if successful. This should only be called within the callback registered by
-      * setActiveServicesCallback.
-      */
-     bool tryUnregister();
+    /**
+     * Try to unregister all services previously registered with 'registerService'.
+     * Returns 'true' if successful. This should only be called within the callback registered by
+     * setActiveServicesCallback.
+     */
+    LIBBINDER_EXPORTED bool tryUnregister();
 
-     /**
-      * Re-register services that were unregistered by 'tryUnregister'.
-      * This method should be called in the case 'tryUnregister' fails
-      * (and should be called on the same thread).
-      */
-     void reRegister();
+    /**
+     * Re-register services that were unregistered by 'tryUnregister'.
+     * This method should be called in the case 'tryUnregister' fails
+     * (and should be called on the same thread).
+     */
+    LIBBINDER_EXPORTED void reRegister();
 
-     /**
-      * Create a second instance of lazy service registrar.
-      *
-      * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar
-      * should be single-instanced, so that the service will only
-      * shut down when all services are unused. A separate instance
-      * is only used to test race conditions.
-      */
-     static LazyServiceRegistrar createExtraTestInstance();
+    /**
+     * Create a second instance of lazy service registrar.
+     *
+     * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar
+     * should be single-instanced, so that the service will only
+     * shut down when all services are unused. A separate instance
+     * is only used to test race conditions.
+     */
+    LIBBINDER_EXPORTED static LazyServiceRegistrar createExtraTestInstance();
 
- private:
-     std::shared_ptr<internal::ClientCounterCallback> mClientCC;
-     LazyServiceRegistrar();
+private:
+    std::shared_ptr<internal::ClientCounterCallback> mClientCC;
+    LazyServiceRegistrar();
 };
 
 }  // namespace binder
diff --git a/libs/binder/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
index 61a029c..04cd1a4 100644
--- a/libs/binder/include/binder/MemoryBase.h
+++ b/libs/binder/include/binder/MemoryBase.h
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
+#include <binder/Common.h>
 #include <binder/IMemory.h>
 
 
@@ -26,8 +27,7 @@
 
 // ---------------------------------------------------------------------------
 
-class MemoryBase : public BnMemory 
-{
+class LIBBINDER_EXPORTED MemoryBase : public BnMemory {
 public:
     MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
     virtual ~MemoryBase();
diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
index 3f7dd11..b979da5 100644
--- a/libs/binder/include/binder/MemoryDealer.h
+++ b/libs/binder/include/binder/MemoryDealer.h
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <binder/Common.h>
 #include <binder/IMemory.h>
 #include <binder/MemoryHeapBase.h>
 
@@ -29,33 +30,32 @@
 
 // ----------------------------------------------------------------------------
 
-class MemoryDealer : public RefBase
-{
+class MemoryDealer : public RefBase {
 public:
-    explicit MemoryDealer(size_t size, const char* name = nullptr,
-            uint32_t flags = 0 /* or bits such as MemoryHeapBase::READ_ONLY */ );
+    LIBBINDER_EXPORTED explicit MemoryDealer(
+            size_t size, const char* name = nullptr,
+            uint32_t flags = 0 /* or bits such as MemoryHeapBase::READ_ONLY */);
 
-    virtual sp<IMemory> allocate(size_t size);
-    virtual void        dump(const char* what) const;
+    LIBBINDER_EXPORTED virtual sp<IMemory> allocate(size_t size);
+    LIBBINDER_EXPORTED virtual void dump(const char* what) const;
 
     // allocations are aligned to some value. return that value so clients can account for it.
-    static size_t      getAllocationAlignment();
+    LIBBINDER_EXPORTED static size_t getAllocationAlignment();
 
     sp<IMemoryHeap> getMemoryHeap() const { return heap(); }
 
 protected:
-    virtual ~MemoryDealer();
+    LIBBINDER_EXPORTED virtual ~MemoryDealer();
 
 private:
     friend class Allocation;
     virtual void                deallocate(size_t offset);
-    const sp<IMemoryHeap>&      heap() const;
+    LIBBINDER_EXPORTED const sp<IMemoryHeap>& heap() const;
     SimpleBestFitAllocator*     allocator() const;
 
     sp<IMemoryHeap>             mHeap;
     SimpleBestFitAllocator*     mAllocator;
 };
 
-
 // ----------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index c7177bd..ff2d09f 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
+#include <binder/Common.h>
 #include <binder/IMemory.h>
 
 
@@ -26,8 +27,7 @@
 
 // ---------------------------------------------------------------------------
 
-class MemoryHeapBase : public BnMemoryHeap
-{
+class MemoryHeapBase : public BnMemoryHeap {
 public:
     static constexpr auto MEMFD_ALLOW_SEALING_FLAG = 0x00000800;
     enum {
@@ -56,42 +56,44 @@
      * maps the memory referenced by fd. but DOESN'T take ownership
      * of the filedescriptor (it makes a copy with dup()
      */
-    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0);
+    LIBBINDER_EXPORTED MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0);
 
     /*
      * maps memory from the given device
      */
-    explicit MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);
+    LIBBINDER_EXPORTED explicit MemoryHeapBase(const char* device, size_t size = 0,
+                                               uint32_t flags = 0);
 
     /*
      * maps memory from ashmem, with the given name for debugging
      * if the READ_ONLY flag is set, the memory will be writeable by the calling process,
      * but not by others. this is NOT the case with the other ctors.
      */
-    explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr);
+    LIBBINDER_EXPORTED explicit MemoryHeapBase(size_t size, uint32_t flags = 0,
+                                               char const* name = nullptr);
 
-    virtual ~MemoryHeapBase();
+    LIBBINDER_EXPORTED virtual ~MemoryHeapBase();
 
     /* implement IMemoryHeap interface */
-    int         getHeapID() const override;
+    LIBBINDER_EXPORTED int getHeapID() const override;
 
     /* virtual address of the heap. returns MAP_FAILED in case of error */
-    void*       getBase() const override;
+    LIBBINDER_EXPORTED void* getBase() const override;
 
-    size_t      getSize() const override;
-    uint32_t    getFlags() const override;
-    off_t       getOffset() const override;
+    LIBBINDER_EXPORTED size_t getSize() const override;
+    LIBBINDER_EXPORTED uint32_t getFlags() const override;
+    LIBBINDER_EXPORTED off_t getOffset() const override;
 
-    const char*         getDevice() const;
+    LIBBINDER_EXPORTED const char* getDevice() const;
 
     /* this closes this heap -- use carefully */
-    void dispose();
+    LIBBINDER_EXPORTED void dispose();
 
 protected:
-            MemoryHeapBase();
+    LIBBINDER_EXPORTED MemoryHeapBase();
     // init() takes ownership of fd
-    status_t init(int fd, void *base, size_t size,
-            int flags = 0, const char* device = nullptr);
+    LIBBINDER_EXPORTED status_t init(int fd, void* base, size_t size, int flags = 0,
+                                     const char* device = nullptr);
 
 private:
     status_t mapfd(int fd, bool writeableByCaller, size_t size, off_t offset = 0);
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index eb73037..5cc0830 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -34,6 +34,7 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
 
@@ -68,73 +69,74 @@
     class ReadableBlob;
     class WritableBlob;
 
-                        Parcel();
-                        ~Parcel();
-    
-    const uint8_t*      data() const;
-    size_t              dataSize() const;
-    size_t              dataAvail() const;
-    size_t              dataPosition() const;
-    size_t              dataCapacity() const;
-    size_t dataBufferSize() const;
+    LIBBINDER_EXPORTED Parcel();
+    LIBBINDER_EXPORTED ~Parcel();
 
-    status_t            setDataSize(size_t size);
+    LIBBINDER_EXPORTED const uint8_t* data() const;
+    LIBBINDER_EXPORTED size_t dataSize() const;
+    LIBBINDER_EXPORTED size_t dataAvail() const;
+    LIBBINDER_EXPORTED size_t dataPosition() const;
+    LIBBINDER_EXPORTED size_t dataCapacity() const;
+    LIBBINDER_EXPORTED size_t dataBufferSize() const;
+
+    LIBBINDER_EXPORTED status_t setDataSize(size_t size);
 
     // this must only be used to set a data position that was previously returned from
     // dataPosition(). If writes are made, the exact same types of writes must be made (e.g.
     // auto i = p.dataPosition(); p.writeInt32(0); p.setDataPosition(i); p.writeInt32(1);).
     // Writing over objects, such as file descriptors and binders, is not supported.
-    void                setDataPosition(size_t pos) const;
-    status_t            setDataCapacity(size_t size);
+    LIBBINDER_EXPORTED void setDataPosition(size_t pos) const;
+    LIBBINDER_EXPORTED status_t setDataCapacity(size_t size);
 
-    status_t            setData(const uint8_t* buffer, size_t len);
+    LIBBINDER_EXPORTED status_t setData(const uint8_t* buffer, size_t len);
 
-    status_t            appendFrom(const Parcel *parcel,
-                                   size_t start, size_t len);
+    LIBBINDER_EXPORTED status_t appendFrom(const Parcel* parcel, size_t start, size_t len);
 
-    int                 compareData(const Parcel& other);
-    status_t compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset,
-                                size_t length, int* result) const;
+    LIBBINDER_EXPORTED int compareData(const Parcel& other);
+    LIBBINDER_EXPORTED status_t compareDataInRange(size_t thisOffset, const Parcel& other,
+                                                   size_t otherOffset, size_t length,
+                                                   int* result) const;
 
-    bool                allowFds() const;
-    bool                pushAllowFds(bool allowFds);
-    void                restoreAllowFds(bool lastValue);
+    LIBBINDER_EXPORTED bool allowFds() const;
+    LIBBINDER_EXPORTED bool pushAllowFds(bool allowFds);
+    LIBBINDER_EXPORTED void restoreAllowFds(bool lastValue);
 
-    bool                hasFileDescriptors() const;
-    status_t hasBinders(bool* result) const;
-    status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const;
-    status_t hasBindersInRange(size_t offset, size_t length, bool* result) const;
+    LIBBINDER_EXPORTED bool hasFileDescriptors() const;
+    LIBBINDER_EXPORTED status_t hasBinders(bool* result) const;
+    LIBBINDER_EXPORTED status_t hasFileDescriptorsInRange(size_t offset, size_t length,
+                                                          bool* result) const;
+    LIBBINDER_EXPORTED status_t hasBindersInRange(size_t offset, size_t length, bool* result) const;
 
     // returns all binder objects in the Parcel
-    std::vector<sp<IBinder>> debugReadAllStrongBinders() const;
+    LIBBINDER_EXPORTED std::vector<sp<IBinder>> debugReadAllStrongBinders() const;
     // returns all file descriptors in the Parcel
     // does not dup
-    std::vector<int> debugReadAllFileDescriptors() const;
+    LIBBINDER_EXPORTED std::vector<int> debugReadAllFileDescriptors() const;
 
     // Zeros data when reallocating. Other mitigations may be added
     // in the future.
     //
     // WARNING: some read methods may make additional copies of data.
     // In order to verify this, heap dumps should be used.
-    void                markSensitive() const;
+    LIBBINDER_EXPORTED void markSensitive() const;
 
     // For a 'data' Parcel, this should mark the Parcel as being prepared for a
     // transaction on this specific binder object. Based on this, the format of
     // the wire binder protocol may change (data is written differently when it
     // is for an RPC transaction).
-    void markForBinder(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED void markForBinder(const sp<IBinder>& binder);
 
     // Whenever possible, markForBinder should be preferred. This method is
     // called automatically on reply Parcels for RPC transactions.
-    void markForRpc(const sp<RpcSession>& session);
+    LIBBINDER_EXPORTED void markForRpc(const sp<RpcSession>& session);
 
     // Whether this Parcel is written for RPC transactions (after calls to
     // markForBinder or markForRpc).
-    bool isForRpc() const;
+    LIBBINDER_EXPORTED bool isForRpc() const;
 
     // Writes the IPC/RPC header.
-    status_t            writeInterfaceToken(const String16& interface);
-    status_t            writeInterfaceToken(const char16_t* str, size_t len);
+    LIBBINDER_EXPORTED status_t writeInterfaceToken(const String16& interface);
+    LIBBINDER_EXPORTED status_t writeInterfaceToken(const char16_t* str, size_t len);
 
     // Parses the RPC header, returning true if the interface name
     // in the header matches the expected interface from the caller.
@@ -143,101 +145,116 @@
     // propagating the StrictMode policy mask, populating the current
     // IPCThreadState, which as an optimization may optionally be
     // passed in.
-    bool                enforceInterface(const String16& interface,
-                                         IPCThreadState* threadState = nullptr) const;
-    bool                enforceInterface(const char16_t* interface,
-                                         size_t len,
-                                         IPCThreadState* threadState = nullptr) const;
-    bool                checkInterface(IBinder*) const;
+    LIBBINDER_EXPORTED bool enforceInterface(const String16& interface,
+                                             IPCThreadState* threadState = nullptr) const;
+    LIBBINDER_EXPORTED bool enforceInterface(const char16_t* interface, size_t len,
+                                             IPCThreadState* threadState = nullptr) const;
+    LIBBINDER_EXPORTED bool checkInterface(IBinder*) const;
 
     // Verify there are no bytes left to be read on the Parcel.
     // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
-    binder::Status enforceNoDataAvail() const;
+    LIBBINDER_EXPORTED binder::Status enforceNoDataAvail() const;
 
     // This Api is used by fuzzers to skip dataAvail checks.
-    void setEnforceNoDataAvail(bool enforceNoDataAvail);
+    LIBBINDER_EXPORTED void setEnforceNoDataAvail(bool enforceNoDataAvail);
 
     // When fuzzing, we want to remove certain ABI checks that cause significant
     // lost coverage, and we also want to avoid logs that cost too much to write.
-    void setServiceFuzzing();
-    bool isServiceFuzzing() const;
+    LIBBINDER_EXPORTED void setServiceFuzzing();
+    LIBBINDER_EXPORTED bool isServiceFuzzing() const;
 
-    void                freeData();
+    LIBBINDER_EXPORTED void freeData();
 
-    size_t              objectsCount() const;
-    
-    status_t            errorCheck() const;
-    void                setError(status_t err);
-    
-    status_t            write(const void* data, size_t len);
-    void*               writeInplace(size_t len);
-    status_t            writeUnpadded(const void* data, size_t len);
-    status_t            writeInt32(int32_t val);
-    status_t            writeUint32(uint32_t val);
-    status_t            writeInt64(int64_t val);
-    status_t            writeUint64(uint64_t val);
-    status_t            writeFloat(float val);
-    status_t            writeDouble(double val);
-    status_t            writeCString(const char* str);
-    status_t            writeString8(const String8& str);
-    status_t            writeString8(const char* str, size_t len);
-    status_t            writeString16(const String16& str);
-    status_t            writeString16(const std::optional<String16>& str);
-    status_t            writeString16(const std::unique_ptr<String16>& str) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeString16(const char16_t* str, size_t len);
-    status_t            writeStrongBinder(const sp<IBinder>& val);
-    status_t            writeInt32Array(size_t len, const int32_t *val);
-    status_t            writeByteArray(size_t len, const uint8_t *val);
-    status_t            writeBool(bool val);
-    status_t            writeChar(char16_t val);
-    status_t            writeByte(int8_t val);
+    LIBBINDER_EXPORTED size_t objectsCount() const;
+
+    LIBBINDER_EXPORTED status_t errorCheck() const;
+    LIBBINDER_EXPORTED void setError(status_t err);
+
+    LIBBINDER_EXPORTED status_t write(const void* data, size_t len);
+    LIBBINDER_EXPORTED void* writeInplace(size_t len);
+    LIBBINDER_EXPORTED status_t writeUnpadded(const void* data, size_t len);
+    LIBBINDER_EXPORTED status_t writeInt32(int32_t val);
+    LIBBINDER_EXPORTED status_t writeUint32(uint32_t val);
+    LIBBINDER_EXPORTED status_t writeInt64(int64_t val);
+    LIBBINDER_EXPORTED status_t writeUint64(uint64_t val);
+    LIBBINDER_EXPORTED status_t writeFloat(float val);
+    LIBBINDER_EXPORTED status_t writeDouble(double val);
+    LIBBINDER_EXPORTED status_t writeCString(const char* str);
+    LIBBINDER_EXPORTED status_t writeString8(const String8& str);
+    LIBBINDER_EXPORTED status_t writeString8(const char* str, size_t len);
+    LIBBINDER_EXPORTED status_t writeString16(const String16& str);
+    LIBBINDER_EXPORTED status_t writeString16(const std::optional<String16>& str);
+    LIBBINDER_EXPORTED status_t writeString16(const std::unique_ptr<String16>& str)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeString16(const char16_t* str, size_t len);
+    LIBBINDER_EXPORTED status_t writeStrongBinder(const sp<IBinder>& val);
+    LIBBINDER_EXPORTED status_t writeInt32Array(size_t len, const int32_t* val);
+    LIBBINDER_EXPORTED status_t writeByteArray(size_t len, const uint8_t* val);
+    LIBBINDER_EXPORTED status_t writeBool(bool val);
+    LIBBINDER_EXPORTED status_t writeChar(char16_t val);
+    LIBBINDER_EXPORTED status_t writeByte(int8_t val);
 
     // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
-    status_t            writeUtf8AsUtf16(const std::string& str);
-    status_t            writeUtf8AsUtf16(const std::optional<std::string>& str);
-    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeUtf8AsUtf16(const std::string& str);
+    LIBBINDER_EXPORTED status_t writeUtf8AsUtf16(const std::optional<std::string>& str);
+    LIBBINDER_EXPORTED status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str)
+            __attribute__((deprecated("use std::optional version instead")));
 
-    status_t            writeByteVector(const std::optional<std::vector<int8_t>>& val);
-    status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeByteVector(const std::vector<int8_t>& val);
-    status_t            writeByteVector(const std::optional<std::vector<uint8_t>>& val);
-    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeByteVector(const std::vector<uint8_t>& val);
-    status_t            writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
-    status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeInt32Vector(const std::vector<int32_t>& val);
-    status_t            writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
-    status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeInt64Vector(const std::vector<int64_t>& val);
-    status_t            writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
-    status_t            writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeUint64Vector(const std::vector<uint64_t>& val);
-    status_t            writeFloatVector(const std::optional<std::vector<float>>& val);
-    status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeFloatVector(const std::vector<float>& val);
-    status_t            writeDoubleVector(const std::optional<std::vector<double>>& val);
-    status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeDoubleVector(const std::vector<double>& val);
-    status_t            writeBoolVector(const std::optional<std::vector<bool>>& val);
-    status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeBoolVector(const std::vector<bool>& val);
-    status_t            writeCharVector(const std::optional<std::vector<char16_t>>& val);
-    status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeCharVector(const std::vector<char16_t>& val);
-    status_t            writeString16Vector(
-                            const std::optional<std::vector<std::optional<String16>>>& val);
-    status_t            writeString16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeString16Vector(const std::vector<String16>& val);
-    status_t            writeUtf8VectorAsUtf16Vector(
-                            const std::optional<std::vector<std::optional<std::string>>>& val);
-    status_t            writeUtf8VectorAsUtf16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::optional<std::vector<int8_t>>& val);
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::vector<int8_t>& val);
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::optional<std::vector<uint8_t>>& val);
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeByteVector(const std::vector<uint8_t>& val);
+    LIBBINDER_EXPORTED status_t writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
+    LIBBINDER_EXPORTED status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeInt32Vector(const std::vector<int32_t>& val);
+    LIBBINDER_EXPORTED status_t writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
+    LIBBINDER_EXPORTED status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeInt64Vector(const std::vector<int64_t>& val);
+    LIBBINDER_EXPORTED status_t writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
+    LIBBINDER_EXPORTED status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeUint64Vector(const std::vector<uint64_t>& val);
+    LIBBINDER_EXPORTED status_t writeFloatVector(const std::optional<std::vector<float>>& val);
+    LIBBINDER_EXPORTED status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeFloatVector(const std::vector<float>& val);
+    LIBBINDER_EXPORTED status_t writeDoubleVector(const std::optional<std::vector<double>>& val);
+    LIBBINDER_EXPORTED status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeDoubleVector(const std::vector<double>& val);
+    LIBBINDER_EXPORTED status_t writeBoolVector(const std::optional<std::vector<bool>>& val);
+    LIBBINDER_EXPORTED status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeBoolVector(const std::vector<bool>& val);
+    LIBBINDER_EXPORTED status_t writeCharVector(const std::optional<std::vector<char16_t>>& val);
+    LIBBINDER_EXPORTED status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeCharVector(const std::vector<char16_t>& val);
+    LIBBINDER_EXPORTED status_t
+    writeString16Vector(const std::optional<std::vector<std::optional<String16>>>& val);
+    LIBBINDER_EXPORTED status_t
+    writeString16Vector(const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeString16Vector(const std::vector<String16>& val);
+    LIBBINDER_EXPORTED status_t
+    writeUtf8VectorAsUtf16Vector(const std::optional<std::vector<std::optional<std::string>>>& val);
+    LIBBINDER_EXPORTED status_t writeUtf8VectorAsUtf16Vector(
+            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
 
-    status_t            writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
-    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
-    status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
+    LIBBINDER_EXPORTED status_t
+    writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
+    LIBBINDER_EXPORTED status_t
+    writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val)
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
 
     // Write an IInterface or a vector of IInterface's
     template <typename T,
@@ -315,7 +332,7 @@
         return writeData(parcelable);
     }
 
-    status_t            writeParcelable(const Parcelable& parcelable);
+    LIBBINDER_EXPORTED status_t writeParcelable(const Parcelable& parcelable);
 
     template<typename T>
     status_t            write(const Flattenable<T>& val);
@@ -335,40 +352,41 @@
     // descriptors are dup'ed, so it is safe to delete the native_handle
     // when this function returns).
     // Doesn't take ownership of the native_handle.
-    status_t            writeNativeHandle(const native_handle* handle);
+    LIBBINDER_EXPORTED status_t writeNativeHandle(const native_handle* handle);
 #endif
 
     // Place a file descriptor into the parcel.  The given fd must remain
     // valid for the lifetime of the parcel.
     // The Parcel does not take ownership of the given fd unless you ask it to.
-    status_t            writeFileDescriptor(int fd, bool takeOwnership = false);
+    LIBBINDER_EXPORTED status_t writeFileDescriptor(int fd, bool takeOwnership = false);
 
     // Place a file descriptor into the parcel.  A dup of the fd is made, which
     // will be closed once the parcel is destroyed.
-    status_t            writeDupFileDescriptor(int fd);
+    LIBBINDER_EXPORTED status_t writeDupFileDescriptor(int fd);
 
     // Place a Java "parcel file descriptor" into the parcel.  The given fd must remain
     // valid for the lifetime of the parcel.
     // The Parcel does not take ownership of the given fd unless you ask it to.
-    status_t            writeParcelFileDescriptor(int fd, bool takeOwnership = false);
+    LIBBINDER_EXPORTED status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false);
 
     // Place a Java "parcel file descriptor" into the parcel.  A dup of the fd is made, which will
     // be closed once the parcel is destroyed.
-    status_t            writeDupParcelFileDescriptor(int fd);
+    LIBBINDER_EXPORTED status_t writeDupParcelFileDescriptor(int fd);
 
     // Place a file descriptor into the parcel.  This will not affect the
     // semantics of the smart file descriptor. A new descriptor will be
     // created, and will be closed when the parcel is destroyed.
-    status_t writeUniqueFileDescriptor(const binder::unique_fd& fd);
+    LIBBINDER_EXPORTED status_t writeUniqueFileDescriptor(const binder::unique_fd& fd);
 
     // Place a vector of file desciptors into the parcel. Each descriptor is
     // dup'd as in writeDupFileDescriptor
-    status_t writeUniqueFileDescriptorVector(
-            const std::optional<std::vector<binder::unique_fd>>& val);
-    status_t writeUniqueFileDescriptorVector(
-            const std::unique_ptr<std::vector<binder::unique_fd>>& val)
+    LIBBINDER_EXPORTED status_t
+    writeUniqueFileDescriptorVector(const std::optional<std::vector<binder::unique_fd>>& val);
+    LIBBINDER_EXPORTED status_t
+    writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<binder::unique_fd>>& val)
             __attribute__((deprecated("use std::optional version instead")));
-    status_t writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val);
+    LIBBINDER_EXPORTED status_t
+    writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val);
 
     // Writes a blob to the parcel.
     // If the blob is small, then it is stored in-place, otherwise it is
@@ -376,58 +394,60 @@
     // immutable blobs if possible since they may be subsequently transferred between
     // processes without further copying whereas mutable blobs always need to be copied.
     // The caller should call release() on the blob after writing its contents.
-    status_t            writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
+    LIBBINDER_EXPORTED status_t writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob);
 
     // Write an existing immutable blob file descriptor to the parcel.
     // This allows the client to send the same blob to multiple processes
     // as long as it keeps a dup of the blob file descriptor handy for later.
-    status_t            writeDupImmutableBlobFileDescriptor(int fd);
+    LIBBINDER_EXPORTED status_t writeDupImmutableBlobFileDescriptor(int fd);
 
-    status_t            writeObject(const flat_binder_object& val, bool nullMetaData);
+    LIBBINDER_EXPORTED status_t writeObject(const flat_binder_object& val, bool nullMetaData);
 
     // Like Parcel.java's writeNoException().  Just writes a zero int32.
     // Currently the native implementation doesn't do any of the StrictMode
     // stack gathering and serialization that the Java implementation does.
-    status_t            writeNoException();
-    
-    status_t            read(void* outData, size_t len) const;
-    const void*         readInplace(size_t len) const;
-    int32_t             readInt32() const;
-    status_t            readInt32(int32_t *pArg) const;
-    uint32_t            readUint32() const;
-    status_t            readUint32(uint32_t *pArg) const;
-    int64_t             readInt64() const;
-    status_t            readInt64(int64_t *pArg) const;
-    uint64_t            readUint64() const;
-    status_t            readUint64(uint64_t *pArg) const;
-    float               readFloat() const;
-    status_t            readFloat(float *pArg) const;
-    double              readDouble() const;
-    status_t            readDouble(double *pArg) const;
-    bool                readBool() const;
-    status_t            readBool(bool *pArg) const;
-    char16_t            readChar() const;
-    status_t            readChar(char16_t *pArg) const;
-    int8_t              readByte() const;
-    status_t            readByte(int8_t *pArg) const;
+    LIBBINDER_EXPORTED status_t writeNoException();
+
+    LIBBINDER_EXPORTED status_t read(void* outData, size_t len) const;
+    LIBBINDER_EXPORTED const void* readInplace(size_t len) const;
+    LIBBINDER_EXPORTED int32_t readInt32() const;
+    LIBBINDER_EXPORTED status_t readInt32(int32_t* pArg) const;
+    LIBBINDER_EXPORTED uint32_t readUint32() const;
+    LIBBINDER_EXPORTED status_t readUint32(uint32_t* pArg) const;
+    LIBBINDER_EXPORTED int64_t readInt64() const;
+    LIBBINDER_EXPORTED status_t readInt64(int64_t* pArg) const;
+    LIBBINDER_EXPORTED uint64_t readUint64() const;
+    LIBBINDER_EXPORTED status_t readUint64(uint64_t* pArg) const;
+    LIBBINDER_EXPORTED float readFloat() const;
+    LIBBINDER_EXPORTED status_t readFloat(float* pArg) const;
+    LIBBINDER_EXPORTED double readDouble() const;
+    LIBBINDER_EXPORTED status_t readDouble(double* pArg) const;
+    LIBBINDER_EXPORTED bool readBool() const;
+    LIBBINDER_EXPORTED status_t readBool(bool* pArg) const;
+    LIBBINDER_EXPORTED char16_t readChar() const;
+    LIBBINDER_EXPORTED status_t readChar(char16_t* pArg) const;
+    LIBBINDER_EXPORTED int8_t readByte() const;
+    LIBBINDER_EXPORTED status_t readByte(int8_t* pArg) const;
 
     // Read a UTF16 encoded string, convert to UTF8
-    status_t            readUtf8FromUtf16(std::string* str) const;
-    status_t            readUtf8FromUtf16(std::optional<std::string>* str) const;
-    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::string* str) const;
+    LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::optional<std::string>* str) const;
+    LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const
+            __attribute__((deprecated("use std::optional version instead")));
 
-    const char*         readCString() const;
-    String8             readString8() const;
-    status_t            readString8(String8* pArg) const;
-    const char*         readString8Inplace(size_t* outLen) const;
-    String16            readString16() const;
-    status_t            readString16(String16* pArg) const;
-    status_t            readString16(std::optional<String16>* pArg) const;
-    status_t            readString16(std::unique_ptr<String16>* pArg) const __attribute__((deprecated("use std::optional version instead")));
-    const char16_t*     readString16Inplace(size_t* outLen) const;
-    sp<IBinder>         readStrongBinder() const;
-    status_t            readStrongBinder(sp<IBinder>* val) const;
-    status_t            readNullableStrongBinder(sp<IBinder>* val) const;
+    LIBBINDER_EXPORTED const char* readCString() const;
+    LIBBINDER_EXPORTED String8 readString8() const;
+    LIBBINDER_EXPORTED status_t readString8(String8* pArg) const;
+    LIBBINDER_EXPORTED const char* readString8Inplace(size_t* outLen) const;
+    LIBBINDER_EXPORTED String16 readString16() const;
+    LIBBINDER_EXPORTED status_t readString16(String16* pArg) const;
+    LIBBINDER_EXPORTED status_t readString16(std::optional<String16>* pArg) const;
+    LIBBINDER_EXPORTED status_t readString16(std::unique_ptr<String16>* pArg) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED const char16_t* readString16Inplace(size_t* outLen) const;
+    LIBBINDER_EXPORTED sp<IBinder> readStrongBinder() const;
+    LIBBINDER_EXPORTED status_t readStrongBinder(sp<IBinder>* val) const;
+    LIBBINDER_EXPORTED status_t readNullableStrongBinder(sp<IBinder>* val) const;
 
     // Read an Enum vector with underlying type int8_t.
     // Does not use padding; each byte is contiguous.
@@ -466,7 +486,7 @@
     status_t            readParcelableVector(std::vector<T>* val) const
             { return readData(val); }
 
-    status_t            readParcelable(Parcelable* parcelable) const;
+    LIBBINDER_EXPORTED status_t readParcelable(Parcelable* parcelable) const;
 
     template<typename T>
     status_t            readParcelable(std::optional<T>* parcelable) const
@@ -484,9 +504,12 @@
     template<typename T>
     status_t            readNullableStrongBinder(sp<T>* val) const;
 
-    status_t            readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
-    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
     template <typename T,
               std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
     status_t readStrongBinderVector(std::vector<sp<T>>* val) const {
@@ -498,43 +521,54 @@
         return readData(val);
     }
 
-    status_t            readByteVector(std::optional<std::vector<int8_t>>* val) const;
-    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readByteVector(std::vector<int8_t>* val) const;
-    status_t            readByteVector(std::optional<std::vector<uint8_t>>* val) const;
-    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readByteVector(std::vector<uint8_t>* val) const;
-    status_t            readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
-    status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readInt32Vector(std::vector<int32_t>* val) const;
-    status_t            readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
-    status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readInt64Vector(std::vector<int64_t>* val) const;
-    status_t            readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
-    status_t            readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readUint64Vector(std::vector<uint64_t>* val) const;
-    status_t            readFloatVector(std::optional<std::vector<float>>* val) const;
-    status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readFloatVector(std::vector<float>* val) const;
-    status_t            readDoubleVector(std::optional<std::vector<double>>* val) const;
-    status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readDoubleVector(std::vector<double>* val) const;
-    status_t            readBoolVector(std::optional<std::vector<bool>>* val) const;
-    status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readBoolVector(std::vector<bool>* val) const;
-    status_t            readCharVector(std::optional<std::vector<char16_t>>* val) const;
-    status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readCharVector(std::vector<char16_t>* val) const;
-    status_t            readString16Vector(
-                            std::optional<std::vector<std::optional<String16>>>* val) const;
-    status_t            readString16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readString16Vector(std::vector<String16>* val) const;
-    status_t            readUtf8VectorFromUtf16Vector(
-                            std::optional<std::vector<std::optional<std::string>>>* val) const;
-    status_t            readUtf8VectorFromUtf16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
+    LIBBINDER_EXPORTED status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readByteVector(std::vector<int8_t>* val) const;
+    LIBBINDER_EXPORTED status_t readByteVector(std::optional<std::vector<uint8_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readByteVector(std::vector<uint8_t>* val) const;
+    LIBBINDER_EXPORTED status_t readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readInt32Vector(std::vector<int32_t>* val) const;
+    LIBBINDER_EXPORTED status_t readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readInt64Vector(std::vector<int64_t>* val) const;
+    LIBBINDER_EXPORTED status_t readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readUint64Vector(std::vector<uint64_t>* val) const;
+    LIBBINDER_EXPORTED status_t readFloatVector(std::optional<std::vector<float>>* val) const;
+    LIBBINDER_EXPORTED status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readFloatVector(std::vector<float>* val) const;
+    LIBBINDER_EXPORTED status_t readDoubleVector(std::optional<std::vector<double>>* val) const;
+    LIBBINDER_EXPORTED status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readDoubleVector(std::vector<double>* val) const;
+    LIBBINDER_EXPORTED status_t readBoolVector(std::optional<std::vector<bool>>* val) const;
+    LIBBINDER_EXPORTED status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readBoolVector(std::vector<bool>* val) const;
+    LIBBINDER_EXPORTED status_t readCharVector(std::optional<std::vector<char16_t>>* val) const;
+    LIBBINDER_EXPORTED status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readCharVector(std::vector<char16_t>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readString16Vector(std::optional<std::vector<std::optional<String16>>>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readString16Vector(std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readString16Vector(std::vector<String16>* val) const;
+    LIBBINDER_EXPORTED status_t readUtf8VectorFromUtf16Vector(
+            std::optional<std::vector<std::optional<std::string>>>* val) const;
+    LIBBINDER_EXPORTED status_t readUtf8VectorFromUtf16Vector(
+            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
     template <typename T, size_t N>
     status_t readFixedArray(std::array<T, N>* val) const {
@@ -564,56 +598,58 @@
     // code on exceptions, but also deals with skipping over rich
     // response headers.  Callers should use this to read & parse the
     // response headers rather than doing it by hand.
-    int32_t             readExceptionCode() const;
+    LIBBINDER_EXPORTED int32_t readExceptionCode() const;
 
 #ifndef BINDER_DISABLE_NATIVE_HANDLE
     // Retrieve native_handle from the parcel. This returns a copy of the
     // parcel's native_handle (the caller takes ownership). The caller
     // must free the native_handle with native_handle_close() and
     // native_handle_delete().
-    native_handle*     readNativeHandle() const;
+    LIBBINDER_EXPORTED native_handle* readNativeHandle() const;
 #endif
 
     // Retrieve a file descriptor from the parcel.  This returns the raw fd
     // in the parcel, which you do not own -- use dup() to get your own copy.
-    int                 readFileDescriptor() const;
+    LIBBINDER_EXPORTED int readFileDescriptor() const;
 
     // Retrieve a Java "parcel file descriptor" from the parcel.  This returns the raw fd
     // in the parcel, which you do not own -- use dup() to get your own copy.
-    int                 readParcelFileDescriptor() const;
+    LIBBINDER_EXPORTED int readParcelFileDescriptor() const;
 
     // Retrieve a smart file descriptor from the parcel.
-    status_t readUniqueFileDescriptor(binder::unique_fd* val) const;
+    LIBBINDER_EXPORTED status_t readUniqueFileDescriptor(binder::unique_fd* val) const;
 
     // Retrieve a Java "parcel file descriptor" from the parcel.
-    status_t readUniqueParcelFileDescriptor(binder::unique_fd* val) const;
+    LIBBINDER_EXPORTED status_t readUniqueParcelFileDescriptor(binder::unique_fd* val) const;
 
     // Retrieve a vector of smart file descriptors from the parcel.
-    status_t readUniqueFileDescriptorVector(
-            std::optional<std::vector<binder::unique_fd>>* val) const;
-    status_t readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val)
-            const __attribute__((deprecated("use std::optional version instead")));
-    status_t readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readUniqueFileDescriptorVector(std::optional<std::vector<binder::unique_fd>>* val) const;
+    LIBBINDER_EXPORTED status_t
+    readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val) const
+            __attribute__((deprecated("use std::optional version instead")));
+    LIBBINDER_EXPORTED status_t
+    readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const;
 
     // Reads a blob from the parcel.
     // The caller should call release() on the blob after reading its contents.
-    status_t            readBlob(size_t len, ReadableBlob* outBlob) const;
+    LIBBINDER_EXPORTED status_t readBlob(size_t len, ReadableBlob* outBlob) const;
 
-    const flat_binder_object* readObject(bool nullMetaData) const;
+    LIBBINDER_EXPORTED const flat_binder_object* readObject(bool nullMetaData) const;
 
     // Explicitly close all file descriptors in the parcel.
-    void                closeFileDescriptors();
+    LIBBINDER_EXPORTED void closeFileDescriptors();
 
     // Debugging: get metrics on current allocations.
-    static size_t       getGlobalAllocSize();
-    static size_t       getGlobalAllocCount();
+    LIBBINDER_EXPORTED static size_t getGlobalAllocSize();
+    LIBBINDER_EXPORTED static size_t getGlobalAllocCount();
 
-    bool                replaceCallingWorkSourceUid(uid_t uid);
+    LIBBINDER_EXPORTED bool replaceCallingWorkSourceUid(uid_t uid);
     // Returns the work source provided by the caller. This can only be trusted for trusted calling
     // uid.
-    uid_t               readCallingWorkSourceUid() const;
+    LIBBINDER_EXPORTED uid_t readCallingWorkSourceUid() const;
 
-    void print(std::ostream& to, uint32_t flags = 0) const;
+    LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
 
 private:
     // `objects` and `objectsSize` always 0 for RPC Parcels.
@@ -660,7 +696,7 @@
     status_t            flattenBinder(const sp<IBinder>& binder);
     status_t            unflattenBinder(sp<IBinder>* out) const;
 
-    status_t readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const;
+    LIBBINDER_EXPORTED status_t readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const;
 
     template<class T>
     status_t            readAligned(T *pArg) const;
@@ -1356,14 +1392,14 @@
 
     class Blob {
     public:
-        Blob();
-        ~Blob();
+        LIBBINDER_EXPORTED Blob();
+        LIBBINDER_EXPORTED ~Blob();
 
-        void clear();
-        void release();
-        inline size_t size() const { return mSize; }
-        inline int fd() const { return mFd; }
-        inline bool isMutable() const { return mMutable; }
+        LIBBINDER_EXPORTED void clear();
+        LIBBINDER_EXPORTED void release();
+        LIBBINDER_EXPORTED inline size_t size() const { return mSize; }
+        LIBBINDER_EXPORTED inline int fd() const { return mFd; }
+        LIBBINDER_EXPORTED inline bool isMutable() const { return mMutable; }
 
     protected:
         void init(int fd, void* data, size_t size, bool isMutable);
@@ -1381,7 +1417,7 @@
 
     // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects
     // following Flattenable template/protocol.
-    class FlattenableHelperInterface {
+    class LIBBINDER_EXPORTED FlattenableHelperInterface {
     protected:
         ~FlattenableHelperInterface() { }
     public:
@@ -1420,21 +1456,21 @@
             return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count);
         }
     };
-    status_t write(const FlattenableHelperInterface& val);
-    status_t read(FlattenableHelperInterface& val) const;
+    LIBBINDER_EXPORTED status_t write(const FlattenableHelperInterface& val);
+    LIBBINDER_EXPORTED status_t read(FlattenableHelperInterface& val) const;
 
 public:
     class ReadableBlob : public Blob {
         friend class Parcel;
     public:
-        inline const void* data() const { return mData; }
-        inline void* mutableData() { return isMutable() ? mData : nullptr; }
+        LIBBINDER_EXPORTED inline const void* data() const { return mData; }
+        LIBBINDER_EXPORTED inline void* mutableData() { return isMutable() ? mData : nullptr; }
     };
 
     class WritableBlob : public Blob {
         friend class Parcel;
     public:
-        inline void* data() { return mData; }
+        LIBBINDER_EXPORTED inline void* data() { return mData; }
     };
 
     /**
@@ -1444,12 +1480,12 @@
      * is referenced by this Parcel, but which this parcel doesn't own (e.g.
      * writeFileDescriptor is called without 'takeOwnership' true).
      */
-    size_t getOpenAshmemSize() const;
+    LIBBINDER_EXPORTED size_t getOpenAshmemSize() const;
 
 private:
     // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
     // this
-    size_t getBlobAshmemSize() const;
+    LIBBINDER_EXPORTED size_t getBlobAshmemSize() const;
 
     // Needed so that we can save object metadata to the disk
     friend class android::binder::debug::RecordedTransaction;
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index c4ef354..50a1d39 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <binder/unique_fd.h>
@@ -26,7 +27,7 @@
 /*
  * C++ implementation of the Java class android.os.ParcelFileDescriptor
  */
-class ParcelFileDescriptor : public android::Parcelable {
+class LIBBINDER_EXPORTED ParcelFileDescriptor : public android::Parcelable {
 public:
     ParcelFileDescriptor();
     explicit ParcelFileDescriptor(binder::unique_fd fd);
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index 2c652be..0b707b3 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -21,6 +21,8 @@
 #include <utils/Errors.h>
 #include <utils/String16.h>
 
+#include <binder/Common.h>
+
 namespace android {
 
 class Parcel;
@@ -31,7 +33,7 @@
 #endif
 
 // Abstract interface of all parcelables.
-class Parcelable {
+class LIBBINDER_EXPORTED Parcelable {
 public:
     virtual ~Parcelable() = default;
 
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 40fd30a..965d097 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <utils/String16.h>
@@ -28,7 +29,7 @@
 /*
  * C++ implementation of the Java class android.os.ParcelableHolder
  */
-class ParcelableHolder : public android::Parcelable {
+class LIBBINDER_EXPORTED ParcelableHolder : public android::Parcelable {
 public:
     ParcelableHolder() = delete;
     explicit ParcelableHolder(Stability stability) : mStability(stability){}
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 21aa705..9c08016 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -24,6 +24,7 @@
 #include <utils/String16.h>
 #include <utils/Singleton.h>
 #include <utils/SortedVector.h>
+#include <binder/Common.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -64,17 +65,17 @@
     void cache(const String16& permission, uid_t uid, bool granted);
 
 public:
-    PermissionCache();
+    LIBBINDER_EXPORTED PermissionCache();
 
-    static bool checkCallingPermission(const String16& permission);
+    LIBBINDER_EXPORTED static bool checkCallingPermission(const String16& permission);
 
-    static bool checkCallingPermission(const String16& permission,
-                                int32_t* outPid, int32_t* outUid);
+    LIBBINDER_EXPORTED static bool checkCallingPermission(const String16& permission,
+                                                          int32_t* outPid, int32_t* outUid);
 
-    static bool checkPermission(const String16& permission,
-            pid_t pid, uid_t uid);
+    LIBBINDER_EXPORTED static bool checkPermission(const String16& permission, pid_t pid,
+                                                   uid_t uid);
 
-    static void purgeCache();
+    LIBBINDER_EXPORTED static void purgeCache();
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h
index 6f9eb5e..0cf0b70 100644
--- a/libs/binder/include/binder/PermissionController.h
+++ b/libs/binder/include/binder/PermissionController.h
@@ -18,14 +18,14 @@
 
 #ifndef __ANDROID_VNDK__
 
+#include <binder/Common.h>
 #include <binder/IPermissionController.h>
 #include <utils/Mutex.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
 
-class PermissionController
-{
+class PermissionController {
 public:
 
     enum {
@@ -42,13 +42,13 @@
         MODE_DEFAULT = 3,
     };
 
-    PermissionController();
+    LIBBINDER_EXPORTED PermissionController();
 
-    bool checkPermission(const String16& permission, int32_t pid, int32_t uid);
-    int32_t noteOp(const String16& op, int32_t uid, const String16& packageName);
-    void getPackagesForUid(const uid_t uid, Vector<String16>& packages);
-    bool isRuntimePermission(const String16& permission);
-    int getPackageUid(const String16& package, int flags);
+    LIBBINDER_EXPORTED bool checkPermission(const String16& permission, int32_t pid, int32_t uid);
+    LIBBINDER_EXPORTED int32_t noteOp(const String16& op, int32_t uid, const String16& packageName);
+    LIBBINDER_EXPORTED void getPackagesForUid(const uid_t uid, Vector<String16>& packages);
+    LIBBINDER_EXPORTED bool isRuntimePermission(const String16& permission);
+    LIBBINDER_EXPORTED int getPackageUid(const String16& package, int flags);
 
 private:
     Mutex mLock;
diff --git a/libs/binder/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
index 4517cf2..7c3b625 100644
--- a/libs/binder/include/binder/PersistableBundle.h
+++ b/libs/binder/include/binder/PersistableBundle.h
@@ -20,6 +20,7 @@
 #include <set>
 #include <vector>
 
+#include <binder/Common.h>
 #include <binder/Parcelable.h>
 #include <utils/String16.h>
 #include <utils/StrongPointer.h>
@@ -32,7 +33,7 @@
  * C++ implementation of PersistableBundle, a mapping from String values to
  * various types that can be saved to persistent and later restored.
  */
-class PersistableBundle : public Parcelable {
+class LIBBINDER_EXPORTED PersistableBundle : public Parcelable {
 public:
     PersistableBundle() = default;
     virtual ~PersistableBundle() = default;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 3672702..a466638 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -35,10 +36,10 @@
  */
 class ProcessState : public virtual RefBase {
 public:
-    static sp<ProcessState> self();
-    static sp<ProcessState> selfOrNull();
+    LIBBINDER_EXPORTED static sp<ProcessState> self();
+    LIBBINDER_EXPORTED static sp<ProcessState> selfOrNull();
 
-    static bool isVndservicemanagerEnabled();
+    LIBBINDER_EXPORTED static bool isVndservicemanagerEnabled();
 
     /* initWithDriver() can be used to configure libbinder to use
      * a different binder driver dev node. It must be called *before*
@@ -48,9 +49,9 @@
      *
      * If this is called with nullptr, the behavior is the same as selfOrNull.
      */
-    static sp<ProcessState> initWithDriver(const char* driver);
+    LIBBINDER_EXPORTED static sp<ProcessState> initWithDriver(const char* driver);
 
-    sp<IBinder> getContextObject(const sp<IBinder>& caller);
+    LIBBINDER_EXPORTED sp<IBinder> getContextObject(const sp<IBinder>& caller);
 
     // This should be called before startThreadPool at the beginning
     // of a program, and libraries should never call it because programs
@@ -60,7 +61,7 @@
     // The 'maxThreads' value refers to the total number of threads
     // that will be started by the kernel. This is in addition to any
     // threads started by 'startThreadPool' or 'joinRpcThreadpool'.
-    status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+    LIBBINDER_EXPORTED status_t setThreadPoolMaxThreadCount(size_t maxThreads);
 
     // Libraries should not call this, as processes should configure
     // threadpools themselves. Should be called in the main function
@@ -72,27 +73,27 @@
     // For instance, if setThreadPoolMaxCount(3) is called and
     // startThreadpPool (+1 thread) and joinThreadPool (+1 thread)
     // are all called, then up to 5 threads can be started.
-    void startThreadPool();
+    LIBBINDER_EXPORTED void startThreadPool();
 
-    [[nodiscard]] bool becomeContextManager();
+    [[nodiscard]] LIBBINDER_EXPORTED bool becomeContextManager();
 
-    sp<IBinder> getStrongProxyForHandle(int32_t handle);
-    void expungeHandle(int32_t handle, IBinder* binder);
+    LIBBINDER_EXPORTED sp<IBinder> getStrongProxyForHandle(int32_t handle);
+    LIBBINDER_EXPORTED void expungeHandle(int32_t handle, IBinder* binder);
 
     // TODO: deprecate.
-    void spawnPooledThread(bool isMain);
+    LIBBINDER_EXPORTED void spawnPooledThread(bool isMain);
 
-    status_t enableOnewaySpamDetection(bool enable);
+    LIBBINDER_EXPORTED status_t enableOnewaySpamDetection(bool enable);
 
     // Set the name of the current thread to look like a threadpool
     // thread. Typically this is called before joinThreadPool.
     //
     // TODO: remove this API, and automatically set it intelligently.
-    void giveThreadPoolName();
+    LIBBINDER_EXPORTED void giveThreadPoolName();
 
-    String8 getDriverName();
+    LIBBINDER_EXPORTED String8 getDriverName();
 
-    ssize_t getKernelReferences(size_t count, uintptr_t* buf);
+    LIBBINDER_EXPORTED ssize_t getKernelReferences(size_t count, uintptr_t* buf);
 
     // Only usable by the context manager.
     // This refcount includes:
@@ -100,7 +101,7 @@
     // 2. Temporary strong references held by the kernel during a
     //    transaction on the node.
     // It does NOT include local strong references to the node
-    ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
+    LIBBINDER_EXPORTED ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
 
     enum class CallRestriction {
         // all calls okay
@@ -112,26 +113,26 @@
     };
     // Sets calling restrictions for all transactions in this process. This must be called
     // before any threads are spawned.
-    void setCallRestriction(CallRestriction restriction);
+    LIBBINDER_EXPORTED void setCallRestriction(CallRestriction restriction);
 
     /**
      * Get the max number of threads that have joined the thread pool.
      * This includes kernel started threads, user joined threads and polling
      * threads if used.
      */
-    size_t getThreadPoolMaxTotalThreadCount() const;
+    LIBBINDER_EXPORTED size_t getThreadPoolMaxTotalThreadCount() const;
 
     /**
      * Check to see if the thread pool has started.
      */
-    bool isThreadPoolStarted() const;
+    LIBBINDER_EXPORTED bool isThreadPoolStarted() const;
 
     enum class DriverFeature {
         ONEWAY_SPAM_DETECTION,
         EXTENDED_ERROR,
     };
     // Determine whether a feature is supported by the binder driver.
-    static bool isDriverFeatureEnabled(const DriverFeature feature);
+    LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature);
 
 private:
     static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h
index f0bee7f..ed75e43 100644
--- a/libs/binder/include/binder/RecordedTransaction.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/Parcel.h>
 #include <binder/unique_fd.h>
 #include <mutex>
@@ -32,25 +33,25 @@
 public:
     // Filled with the first transaction from fd.
 
-    static std::optional<RecordedTransaction> fromFile(const binder::unique_fd& fd);
+    LIBBINDER_EXPORTED static std::optional<RecordedTransaction> fromFile(
+            const binder::unique_fd& fd);
     // Filled with the arguments.
-    static std::optional<RecordedTransaction> fromDetails(const String16& interfaceName,
-                                                          uint32_t code, uint32_t flags,
-                                                          timespec timestamp, const Parcel& data,
-                                                          const Parcel& reply, status_t err);
-    RecordedTransaction(RecordedTransaction&& t) noexcept;
+    LIBBINDER_EXPORTED static std::optional<RecordedTransaction> fromDetails(
+            const String16& interfaceName, uint32_t code, uint32_t flags, timespec timestamp,
+            const Parcel& data, const Parcel& reply, status_t err);
+    LIBBINDER_EXPORTED RecordedTransaction(RecordedTransaction&& t) noexcept;
 
-    [[nodiscard]] status_t dumpToFile(const binder::unique_fd& fd) const;
+    [[nodiscard]] LIBBINDER_EXPORTED status_t dumpToFile(const binder::unique_fd& fd) const;
 
-    const std::string& getInterfaceName() const;
-    uint32_t getCode() const;
-    uint32_t getFlags() const;
-    int32_t getReturnedStatus() const;
-    timespec getTimestamp() const;
-    uint32_t getVersion() const;
-    const Parcel& getDataParcel() const;
-    const Parcel& getReplyParcel() const;
-    const std::vector<uint64_t>& getObjectOffsets() const;
+    LIBBINDER_EXPORTED const std::string& getInterfaceName() const;
+    LIBBINDER_EXPORTED uint32_t getCode() const;
+    LIBBINDER_EXPORTED uint32_t getFlags() const;
+    LIBBINDER_EXPORTED int32_t getReturnedStatus() const;
+    LIBBINDER_EXPORTED timespec getTimestamp() const;
+    LIBBINDER_EXPORTED uint32_t getVersion() const;
+    LIBBINDER_EXPORTED const Parcel& getDataParcel() const;
+    LIBBINDER_EXPORTED const Parcel& getReplyParcel() const;
+    LIBBINDER_EXPORTED const std::vector<uint64_t>& getObjectOffsets() const;
 
 private:
     RecordedTransaction() = default;
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index a07880d..abea0fb 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 #include <binder/RpcSession.h>
 #include <binder/RpcThreads.h>
@@ -47,7 +48,7 @@
  */
 class RpcServer final : public virtual RefBase, private RpcSession::EventListener {
 public:
-    static sp<RpcServer> make(
+    LIBBINDER_EXPORTED static sp<RpcServer> make(
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     /**
@@ -59,7 +60,8 @@
      * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client
      * session can be created from the client end of the pair.
      */
-    [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(binder::unique_fd serverFd);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t
+    setupUnixDomainSocketBootstrapServer(binder::unique_fd serverFd);
 
     /**
      * This represents a session for responses, e.g.:
@@ -69,7 +71,7 @@
      *     process B makes binder b and sends it to A
      *     A uses this 'back session' to send things back to B
      */
-    [[nodiscard]] status_t setupUnixDomainServer(const char* path);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupUnixDomainServer(const char* path);
 
     /**
      * Sets up an RPC server with a raw socket file descriptor.
@@ -79,12 +81,13 @@
      * This method is used in the libbinder_rpc_unstable API
      * RunInitUnixDomainRpcServer().
      */
-    [[nodiscard]] status_t setupRawSocketServer(binder::unique_fd socket_fd);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupRawSocketServer(binder::unique_fd socket_fd);
 
     /**
      * Creates an RPC server binding to the given CID at the given port.
      */
-    [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupVsockServer(unsigned int bindCid,
+                                                               unsigned int port);
 
     /**
      * Creates an RPC server at the current port using IPv4.
@@ -100,24 +103,25 @@
      * "0.0.0.0" allows for connections on any IP address that the device may
      * have
      */
-    [[nodiscard]] status_t setupInetServer(const char* address, unsigned int port,
-                                           unsigned int* assignedPort = nullptr);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupInetServer(const char* address,
+                                                              unsigned int port,
+                                                              unsigned int* assignedPort = nullptr);
 
     /**
      * If setup*Server has been successful, return true. Otherwise return false.
      */
-    [[nodiscard]] bool hasServer();
+    [[nodiscard]] LIBBINDER_EXPORTED bool hasServer();
 
     /**
      * If hasServer(), return the server FD. Otherwise return invalid FD.
      */
-    [[nodiscard]] binder::unique_fd releaseServer();
+    [[nodiscard]] LIBBINDER_EXPORTED binder::unique_fd releaseServer();
 
     /**
      * Set up server using an external FD previously set up by releaseServer().
      * Return false if there's already a server.
      */
-    [[nodiscard]] status_t setupExternalServer(binder::unique_fd serverFd);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupExternalServer(binder::unique_fd serverFd);
 
     /**
      * This must be called before adding a client session. This corresponds
@@ -130,15 +134,15 @@
      * TODO(b/167966510): these are currently created per client, but these
      * should be shared.
      */
-    void setMaxThreads(size_t threads);
-    size_t getMaxThreads();
+    LIBBINDER_EXPORTED void setMaxThreads(size_t threads);
+    LIBBINDER_EXPORTED 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.
      */
-    [[nodiscard]] bool setProtocolVersion(uint32_t version);
+    [[nodiscard]] LIBBINDER_EXPORTED bool setProtocolVersion(uint32_t version);
 
     /**
      * Set the supported transports for sending and receiving file descriptors.
@@ -146,7 +150,7 @@
      * Clients will propose a mode when connecting. If the mode is not in the
      * provided list, the connection will be rejected.
      */
-    void setSupportedFileDescriptorTransportModes(
+    LIBBINDER_EXPORTED void setSupportedFileDescriptorTransportModes(
             const std::vector<RpcSession::FileDescriptorTransportMode>& modes);
 
     /**
@@ -155,11 +159,11 @@
      *
      * Holds a strong reference to the root object.
      */
-    void setRootObject(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED void setRootObject(const sp<IBinder>& binder);
     /**
      * Holds a weak reference to the root object.
      */
-    void setRootObjectWeak(const wp<IBinder>& binder);
+    LIBBINDER_EXPORTED void setRootObjectWeak(const wp<IBinder>& binder);
     /**
      * Allows a root object to be created for each session.
      *
@@ -174,9 +178,9 @@
      *   validate the size, then cast the type-erased pointer to a pointer to the actual type of the
      *   address, e.g., const void* to const sockaddr_vm*.
      */
-    void setPerSessionRootObject(
+    LIBBINDER_EXPORTED void setPerSessionRootObject(
             std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object);
-    sp<IBinder> getRootObject();
+    LIBBINDER_EXPORTED sp<IBinder> getRootObject();
 
     /**
      * Set optional filter of incoming connections based on the peer's address.
@@ -186,24 +190,25 @@
      * See the description of setPerSessionRootObject() for details about
      * the callable's arguments.
      */
-    void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
+    LIBBINDER_EXPORTED void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
 
     /**
      * Set optional modifier of each newly created server socket.
      *
      * The only argument is a successfully created file descriptor, not bound to an address yet.
      */
-    void setServerSocketModifier(std::function<void(binder::borrowed_fd)>&& modifier);
+    LIBBINDER_EXPORTED void setServerSocketModifier(
+            std::function<void(binder::borrowed_fd)>&& modifier);
 
     /**
      * See RpcTransportCtx::getCertificate
      */
-    std::vector<uint8_t> getCertificate(RpcCertificateFormat);
+    LIBBINDER_EXPORTED std::vector<uint8_t> getCertificate(RpcCertificateFormat);
 
     /**
      * Runs join() in a background thread. Immediately returns.
      */
-    void start();
+    LIBBINDER_EXPORTED void start();
 
     /**
      * You must have at least one client session before calling this.
@@ -216,7 +221,7 @@
      * still occurring. To ensure that the service is fully shutdown, you might
      * want to call shutdown after 'join' returns.
      */
-    void join();
+    LIBBINDER_EXPORTED void join();
 
     /**
      * Shut down any existing join(). Return true if successfully shut down, false otherwise
@@ -225,20 +230,20 @@
      *
      * Warning: this will hang if it is called from its own thread.
      */
-    [[nodiscard]] bool shutdown();
+    [[nodiscard]] LIBBINDER_EXPORTED bool shutdown();
 
     /**
      * For debugging!
      */
-    std::vector<sp<RpcSession>> listSessions();
-    size_t numUninitializedSessions();
+    LIBBINDER_EXPORTED std::vector<sp<RpcSession>> listSessions();
+    LIBBINDER_EXPORTED size_t numUninitializedSessions();
 
     /**
      * Whether any requests are currently being processed.
      */
-    bool hasActiveRequests();
+    LIBBINDER_EXPORTED bool hasActiveRequests();
 
-    ~RpcServer();
+    LIBBINDER_EXPORTED ~RpcServer();
 
 private:
     friend RpcServerTrusty;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 11fbde9..40102bb 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 #include <binder/RpcThreads.h>
 #include <binder/RpcTransport.h>
@@ -57,12 +58,13 @@
 class RpcSession final : public virtual RefBase {
 public:
     // Create an RpcSession with default configuration (raw sockets).
-    static sp<RpcSession> make();
+    LIBBINDER_EXPORTED static sp<RpcSession> make();
 
     // Create an RpcSession with the given configuration. |serverRpcCertificateFormat| and
     // |serverCertificate| must have values or be nullopt simultaneously. If they have values, set
     // server certificate.
-    static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
+    LIBBINDER_EXPORTED static sp<RpcSession> make(
+            std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
 
     /**
      * Set the maximum number of incoming threads allowed to be made (for things like callbacks).
@@ -75,8 +77,8 @@
      *
      * TODO(b/189955605): start these lazily - currently all are started
      */
-    void setMaxIncomingThreads(size_t threads);
-    size_t getMaxIncomingThreads();
+    LIBBINDER_EXPORTED void setMaxIncomingThreads(size_t threads);
+    LIBBINDER_EXPORTED size_t getMaxIncomingThreads();
 
     /**
      * Set the maximum number of outgoing connections allowed to be made.
@@ -90,15 +92,15 @@
      * created. This API is used to limit the amount of resources a server can request you
      * create.
      */
-    void setMaxOutgoingConnections(size_t connections);
-    size_t getMaxOutgoingThreads();
+    LIBBINDER_EXPORTED void setMaxOutgoingConnections(size_t connections);
+    LIBBINDER_EXPORTED size_t getMaxOutgoingThreads();
 
     /**
      * 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();
+    [[nodiscard]] LIBBINDER_EXPORTED bool setProtocolVersion(uint32_t version);
+    LIBBINDER_EXPORTED std::optional<uint32_t> getProtocolVersion();
 
     enum class FileDescriptorTransportMode : uint8_t {
         NONE = 0,
@@ -111,29 +113,30 @@
     /**
      * Set the transport for sending and receiving file descriptors.
      */
-    void setFileDescriptorTransportMode(FileDescriptorTransportMode mode);
-    FileDescriptorTransportMode getFileDescriptorTransportMode();
+    LIBBINDER_EXPORTED void setFileDescriptorTransportMode(FileDescriptorTransportMode mode);
+    LIBBINDER_EXPORTED FileDescriptorTransportMode getFileDescriptorTransportMode();
 
     /**
      * This should be called once per thread, matching 'join' in the remote
      * process.
      */
-    [[nodiscard]] status_t setupUnixDomainClient(const char* path);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupUnixDomainClient(const char* path);
 
     /**
      * Connects to an RPC server over a nameless Unix domain socket pair.
      */
-    [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(binder::unique_fd bootstrap);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t
+    setupUnixDomainSocketBootstrapClient(binder::unique_fd bootstrap);
 
     /**
-     * Connects to an RPC server at the CVD & port.
+     * Connects to an RPC server at the CID & port.
      */
-    [[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupVsockClient(unsigned int cid, unsigned int port);
 
     /**
      * Connects to an RPC server at the given address and port.
      */
-    [[nodiscard]] status_t setupInetClient(const char* addr, unsigned int port);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t setupInetClient(const char* addr, unsigned int port);
 
     /**
      * Starts talking to an RPC server which has already been connected to. This
@@ -145,8 +148,8 @@
      *
      * For future compatibility, 'request' should not reference any stack data.
      */
-    [[nodiscard]] status_t setupPreconnectedClient(binder::unique_fd fd,
-                                                   std::function<binder::unique_fd()>&& request);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t
+    setupPreconnectedClient(binder::unique_fd fd, std::function<binder::unique_fd()>&& request);
 
     /**
      * For debugging!
@@ -155,24 +158,24 @@
      * response will never be satisfied. All data sent here will be
      * unceremoniously cast down the bottomless pit, /dev/null.
      */
-    [[nodiscard]] status_t addNullDebuggingClient();
+    [[nodiscard]] LIBBINDER_EXPORTED status_t addNullDebuggingClient();
 
     /**
      * Query the other side of the session for the root object hosted by that
      * process's RpcServer (if one exists)
      */
-    sp<IBinder> getRootObject();
+    LIBBINDER_EXPORTED sp<IBinder> getRootObject();
 
     /**
      * Query the other side of the session for the maximum number of threads
      * it supports (maximum number of concurrent non-nested synchronous transactions)
      */
-    [[nodiscard]] status_t getRemoteMaxThreads(size_t* maxThreads);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t getRemoteMaxThreads(size_t* maxThreads);
 
     /**
      * See RpcTransportCtx::getCertificate
      */
-    std::vector<uint8_t> getCertificate(RpcCertificateFormat);
+    LIBBINDER_EXPORTED std::vector<uint8_t> getCertificate(RpcCertificateFormat);
 
     /**
      * Shuts down the service.
@@ -188,33 +191,34 @@
      * complete before returning. This will hang if it is called from the
      * session threadpool (when processing received calls).
      */
-    [[nodiscard]] bool shutdownAndWait(bool wait);
+    [[nodiscard]] LIBBINDER_EXPORTED bool shutdownAndWait(bool wait);
 
-    [[nodiscard]] status_t transact(const sp<IBinder>& binder, uint32_t code, const Parcel& data,
-                                    Parcel* reply, uint32_t flags);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t transact(const sp<IBinder>& binder, uint32_t code,
+                                                       const Parcel& data, Parcel* reply,
+                                                       uint32_t flags);
 
     /**
      * Generally, you should not call this, unless you are testing error
      * conditions, as this is called automatically by BpBinders when they are
      * deleted (this is also why a raw pointer is used here)
      */
-    [[nodiscard]] status_t sendDecStrong(const BpBinder* binder);
+    [[nodiscard]] LIBBINDER_EXPORTED status_t sendDecStrong(const BpBinder* binder);
 
     /**
      * Whether any requests are currently being processed.
      */
-    bool hasActiveRequests();
+    LIBBINDER_EXPORTED bool hasActiveRequests();
 
-    ~RpcSession();
+    LIBBINDER_EXPORTED ~RpcSession();
 
     /**
      * Server if this session is created as part of a server (symmetrical to
      * client servers). Otherwise, nullptr.
      */
-    sp<RpcServer> server();
+    LIBBINDER_EXPORTED sp<RpcServer> server();
 
     // internal only
-    const std::unique_ptr<RpcState>& state() { return mRpcBinderState; }
+    LIBBINDER_EXPORTED const std::unique_ptr<RpcState>& state() { return mRpcBinderState; }
 
 private:
     friend sp<RpcSession>;
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index d25f292..99fa6b8 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -22,27 +22,29 @@
 #include <memory>
 #include <thread>
 
+#include <binder/Common.h>
+
 namespace android {
 
 #ifdef BINDER_RPC_SINGLE_THREADED
-class RpcMutex {
+class LIBBINDER_EXPORTED RpcMutex {
 public:
     void lock() {}
     void unlock() {}
 };
 
-class RpcMutexUniqueLock {
+class LIBBINDER_EXPORTED RpcMutexUniqueLock {
 public:
     RpcMutexUniqueLock(RpcMutex&) {}
     void unlock() {}
 };
 
-class RpcMutexLockGuard {
+class LIBBINDER_EXPORTED RpcMutexLockGuard {
 public:
     RpcMutexLockGuard(RpcMutex&) {}
 };
 
-class RpcConditionVariable {
+class LIBBINDER_EXPORTED RpcConditionVariable {
 public:
     void notify_one() {}
     void notify_all() {}
@@ -65,7 +67,7 @@
     }
 };
 
-class RpcMaybeThread {
+class LIBBINDER_EXPORTED RpcMaybeThread {
 public:
     RpcMaybeThread() = default;
 
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index a50cdc1..843c4bf 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -27,6 +27,7 @@
 
 #include <utils/Errors.h>
 
+#include <binder/Common.h>
 #include <binder/Functional.h>
 #include <binder/RpcCertificateFormat.h>
 #include <binder/RpcThreads.h>
@@ -51,7 +52,7 @@
 
 // Represents a socket connection.
 // No thread-safety is guaranteed for these APIs.
-class RpcTransport {
+class LIBBINDER_EXPORTED RpcTransport {
 public:
     virtual ~RpcTransport() = default;
 
@@ -123,7 +124,7 @@
 
 // Represents the context that generates the socket connection.
 // All APIs are thread-safe. See RpcTransportCtxRaw and RpcTransportCtxTls for details.
-class RpcTransportCtx {
+class LIBBINDER_EXPORTED RpcTransportCtx {
 public:
     virtual ~RpcTransportCtx() = default;
 
@@ -154,7 +155,7 @@
 
 // A factory class that generates RpcTransportCtx.
 // All APIs are thread-safe.
-class RpcTransportCtxFactory {
+class LIBBINDER_EXPORTED RpcTransportCtxFactory {
 public:
     virtual ~RpcTransportCtxFactory() = default;
     // Creates server context.
@@ -171,7 +172,7 @@
     RpcTransportCtxFactory() = default;
 };
 
-struct RpcTransportFd final {
+struct LIBBINDER_EXPORTED RpcTransportFd final {
 private:
     mutable bool isPolling{false};
 
diff --git a/libs/binder/include/binder/RpcTransportRaw.h b/libs/binder/include/binder/RpcTransportRaw.h
index 6fb1f92..bfa73ea 100644
--- a/libs/binder/include/binder/RpcTransportRaw.h
+++ b/libs/binder/include/binder/RpcTransportRaw.h
@@ -21,6 +21,7 @@
 
 #include <memory>
 
+#include <binder/Common.h>
 #include <binder/RpcTransport.h>
 
 namespace android {
@@ -28,11 +29,11 @@
 // RpcTransportCtxFactory with TLS disabled.
 class RpcTransportCtxFactoryRaw : public RpcTransportCtxFactory {
 public:
-    static std::unique_ptr<RpcTransportCtxFactory> make();
+    LIBBINDER_EXPORTED 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;
+    LIBBINDER_EXPORTED std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+    LIBBINDER_EXPORTED std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+    LIBBINDER_EXPORTED const char* toCString() const override;
 
 private:
     RpcTransportCtxFactoryRaw() = default;
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 96b9733..c671eed 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 
@@ -34,7 +35,7 @@
 namespace SafeInterface {
 
 // ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
-class ParcelHandler {
+class LIBBINDER_EXPORTED ParcelHandler {
 public:
     explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {}
 
@@ -243,7 +244,7 @@
 } // namespace SafeInterface
 
 template <typename Interface>
-class SafeBpInterface : public BpInterface<Interface> {
+class LIBBINDER_EXPORTED SafeBpInterface : public BpInterface<Interface> {
 protected:
     SafeBpInterface(const sp<IBinder>& impl, const char* logTag)
           : BpInterface<Interface>(impl), mLogTag(logTag) {}
@@ -438,7 +439,7 @@
 };
 
 template <typename Interface>
-class SafeBnInterface : public BnInterface<Interface> {
+class LIBBINDER_EXPORTED SafeBnInterface : public BnInterface<Interface> {
 public:
     explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {}
 
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index ce4362f..cafb8aa 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <binder/IBinder.h>
 #include <string>
 
@@ -54,7 +55,7 @@
     // requirements associated with that higher stability level. For instance, a
     // VINTF stability binder is required to be in the VINTF manifest. This API
     // can be called to use that same interface within the local partition.
-    static void forceDowngradeToLocalStability(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED static void forceDowngradeToLocalStability(const sp<IBinder>& binder);
 
     // WARNING: Below APIs are only ever expected to be called by auto-generated code.
     //     Instead of calling them, you should set the stability of a .aidl interface
@@ -76,30 +77,30 @@
     // requirements associated with that higher stability level. For instance, a
     // VINTF stability binder is required to be in the VINTF manifest. This API
     // can be called to use that same interface within the vendor partition.
-    static void forceDowngradeToVendorStability(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED static void forceDowngradeToVendorStability(const sp<IBinder>& binder);
 
     // Given a binder interface at a certain stability, there may be some
     // requirements associated with that higher stability level. For instance, a
     // VINTF stability binder is required to be in the VINTF manifest. This API
     // can be called to use that same interface within the system partition.
-    static void forceDowngradeToSystemStability(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED static void forceDowngradeToSystemStability(const sp<IBinder>& binder);
 
     // WARNING: This is only ever expected to be called by auto-generated code. You likely want to
     // change or modify the stability class of the interface you are using.
     // This must be called as soon as the binder in question is constructed. No thread safety
     // is provided.
     // E.g. stability is according to libbinder compilation unit
-    static void markCompilationUnit(IBinder* binder);
+    LIBBINDER_EXPORTED static void markCompilationUnit(IBinder* binder);
     // WARNING: This is only ever expected to be called by auto-generated code. You likely want to
     // change or modify the stability class of the interface you are using.
     // This must be called as soon as the binder in question is constructed. No thread safety
     // is provided.
     // E.g. stability is according to libbinder_ndk or Java SDK AND the interface
     //     expressed here is guaranteed to be stable for multiple years (Stable AIDL)
-    static void markVintf(IBinder* binder);
+    LIBBINDER_EXPORTED static void markVintf(IBinder* binder);
 
     // WARNING: for debugging only
-    static std::string debugToString(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED static std::string debugToString(const sp<IBinder>& binder);
 
     // WARNING: This is only ever expected to be called by auto-generated code or tests.
     // You likely want to change or modify the stability of the interface you are using.
@@ -109,11 +110,12 @@
     //     expressed here is guaranteed to be stable for multiple years (Stable AIDL)
     // If this is called when __ANDROID_VNDK__ is not defined, then it is UB and will likely
     // break the device during GSI or other tests.
-    static void markVndk(IBinder* binder);
+    LIBBINDER_EXPORTED static void markVndk(IBinder* binder);
 
     // Returns true if the binder needs to be declared in the VINTF manifest or
     // else false if the binder is local to the current partition.
-    static bool requiresVintfDeclaration(const sp<IBinder>& binder);
+    LIBBINDER_EXPORTED static bool requiresVintfDeclaration(const sp<IBinder>& binder);
+
 private:
     // Parcel needs to read/write stability level in an unstable format.
     friend ::android::Parcel;
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index af34695..49ccf7c 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -21,6 +21,7 @@
 #include <sstream> // historical
 #include <ostream>
 
+#include <binder/Common.h>
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 #include <string>
@@ -51,7 +52,7 @@
 //         // exception during handling.
 //     }
 //
-class Status final {
+class LIBBINDER_EXPORTED Status final {
 public:
     // Keep the exception codes in sync with android/os/Parcel.java.
     enum Exception {
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index 50158c3..0527eeb 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <binder/Common.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
 
@@ -26,8 +27,7 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
-class TextOutput
-{
+class LIBBINDER_EXPORTED TextOutput {
 public:
                         TextOutput();
     virtual             ~TextOutput();
@@ -52,17 +52,17 @@
 // DO NOT USE: prefer libutils/libbase logs, which don't require static data to
 // be allocated.
 // Text output stream for printing to the log (via utils/Log.h).
-extern TextOutput& alog;
+extern LIBBINDER_EXPORTED TextOutput& alog;
 
 // DO NOT USE: prefer libutils/libbase logs, which don't require static data to
 // be allocated.
 // Text output stream for printing to stdout.
-extern TextOutput& aout;
+extern LIBBINDER_EXPORTED TextOutput& aout;
 
 // DO NOT USE: prefer libutils/libbase logs, which don't require static data to
 // be allocated.
 // Text output stream for printing to stderr.
-extern TextOutput& aerr;
+extern LIBBINDER_EXPORTED TextOutput& aerr;
 
 typedef TextOutput& (*TextOutputManipFunc)(TextOutput&);
 
@@ -80,10 +80,9 @@
     return to;
 }
 
-TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
+LIBBINDER_EXPORTED TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
 
-class TypeCode
-{
+class LIBBINDER_EXPORTED TypeCode {
 public:
     inline explicit TypeCode(uint32_t code);
     inline ~TypeCode();
@@ -94,10 +93,9 @@
     uint32_t mCode;
 };
 
-std::ostream& operator<<(std::ostream& to, const TypeCode& val);
+LIBBINDER_EXPORTED std::ostream& operator<<(std::ostream& to, const TypeCode& val);
 
-class HexDump
-{
+class LIBBINDER_EXPORTED HexDump {
 public:
     HexDump(const void *buf, size_t size, size_t bytesPerLine=16);
     inline ~HexDump();
@@ -123,7 +121,7 @@
     bool mCArrayStyle;
 };
 
-std::ostream& operator<<(std::ostream& to, const HexDump& val);
+LIBBINDER_EXPORTED std::ostream& operator<<(std::ostream& to, const HexDump& val);
 inline TextOutput& operator<<(TextOutput& to,
                               decltype(std::endl<char,
                                        std::char_traits<char>>)
diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h
index 95318b2..2f450cb 100644
--- a/libs/binder/include/binder/Trace.h
+++ b/libs/binder/include/binder/Trace.h
@@ -22,6 +22,8 @@
 #include <cutils/trace.h>
 #endif
 
+#include <binder/Common.h>
+
 #ifdef ATRACE_TAG_AIDL
 #if ATRACE_TAG_AIDL != (1 << 24)
 #error "Mismatched ATRACE_TAG_AIDL definitions"
@@ -39,9 +41,10 @@
 // libcutils/libutils
 void trace_begin(uint64_t tag, const char* name);
 void trace_end(uint64_t tag);
+void trace_int(uint64_t tag, const char* name, int32_t value);
 } // namespace os
 
-class ScopedTrace {
+class LIBBINDER_EXPORTED ScopedTrace {
 public:
     inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { os::trace_begin(mTag, name); }
 
diff --git a/libs/binder/include/binder/unique_fd.h b/libs/binder/include/binder/unique_fd.h
index 439b8a2..3de4149 100644
--- a/libs/binder/include/binder/unique_fd.h
+++ b/libs/binder/include/binder/unique_fd.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <binder/Common.h>
+
 #ifndef BINDER_NO_LIBBASE
 
 #include <android-base/unique_fd.h>
@@ -43,7 +45,7 @@
 //
 //      return 0; // Descriptor is closed for you.
 //
-class unique_fd final {
+class LIBBINDER_EXPORTED unique_fd final {
 public:
     unique_fd() {}
 
@@ -99,7 +101,7 @@
 // If you think of unique_fd as being like std::string in that represents
 // ownership, borrowed_fd is like std::string_view (and int is like const
 // char*).
-struct borrowed_fd {
+struct LIBBINDER_EXPORTED borrowed_fd {
     /* implicit */ borrowed_fd(int fd) : fd_(fd) {}                      // NOLINT
     /* implicit */ borrowed_fd(const unique_fd& ufd) : fd_(ufd.get()) {} // NOLINT
 
diff --git a/libs/binder/liblog_stub/Android.bp b/libs/binder/liblog_stub/Android.bp
index f2ca22f..2de6658 100644
--- a/libs/binder/liblog_stub/Android.bp
+++ b/libs/binder/liblog_stub/Android.bp
@@ -30,6 +30,7 @@
     product_available: true,
     recovery_available: true,
     vendor_available: true,
+    cmake_snapshot_supported: true,
 
     target: {
         windows: {
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 9a2d14a..26c228d 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -32,17 +32,11 @@
     ],
 }
 
-cc_library {
-    name: "libbinder_ndk",
-
+cc_defaults {
+    name: "libbinder_ndk_common_defaults",
     host_supported: true,
     recovery_available: true,
 
-    llndk: {
-        symbol_file: "libbinder_ndk.map.txt",
-        export_llndk_headers: ["libvendorsupport_llndk_headers"],
-    },
-
     export_include_dirs: [
         "include_cpp",
         "include_ndk",
@@ -50,7 +44,6 @@
     ],
 
     cflags: [
-        "-DBINDER_WITH_KERNEL_IPC",
         "-Wall",
         "-Wextra",
         "-Wextra-semi",
@@ -59,14 +52,48 @@
 
     srcs: [
         "ibinder.cpp",
-        "ibinder_jni.cpp",
         "libbinder.cpp",
         "parcel.cpp",
+        "stability.cpp",
+        "status.cpp",
+    ],
+}
+
+cc_library_host_shared {
+    name: "libbinder_ndk_sdk",
+
+    defaults: [
+        "libbinder_ndk_common_defaults",
+        "binder_sdk_defaults",
+    ],
+    cmake_snapshot_supported: true,
+
+    shared_libs: [
+        "libbinder_sdk",
+        "libutils_binder_sdk",
+    ],
+}
+
+cc_library {
+    name: "libbinder_ndk",
+
+    defaults: ["libbinder_ndk_common_defaults"],
+    cmake_snapshot_supported: false,
+
+    llndk: {
+        symbol_file: "libbinder_ndk.map.txt",
+        export_llndk_headers: ["libvendorsupport_llndk_headers"],
+    },
+
+    cflags: [
+        "-DBINDER_WITH_KERNEL_IPC",
+    ],
+
+    srcs: [
+        "ibinder_jni.cpp",
         "parcel_jni.cpp",
         "persistable_bundle.cpp",
         "process.cpp",
-        "stability.cpp",
-        "status.cpp",
         "service_manager.cpp",
     ],
 
@@ -195,6 +222,7 @@
     host_supported: true,
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
+    cmake_snapshot_supported: true,
     target: {
         darwin: {
             enabled: false,
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 52edae4..41b30a0 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -30,7 +30,11 @@
      * Services with methods that perform file IO, web socket creation or ways to egress data must
      * not be added with this flag for privacy concerns.
      */
-    ADD_SERVICE_ALLOW_ISOLATED = 1,
+    ADD_SERVICE_ALLOW_ISOLATED = 1 << 0,
+    ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1,
+    ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
+    ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
+    ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
 };
 
 /**
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 5529455..4436dbe 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -49,7 +49,25 @@
     sp<IServiceManager> sm = defaultServiceManager();
 
     bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
-    status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated);
+    int dumpFlags = 0;
+    if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL) {
+        dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL;
+    }
+    if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH) {
+        dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_HIGH;
+    }
+    if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL) {
+        dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_NORMAL;
+    }
+    if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) {
+        dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
+    }
+    if (dumpFlags == 0) {
+        dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
+    }
+    status_t exception =
+            sm->addService(String16(instance), binder->getBinder(), allowIsolated, dumpFlags);
+
     return PruneException(exception);
 }
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 725744c..2deb254 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -26,10 +26,7 @@
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.compos",
-        "com.android.rkpd",
-        "com.android.uwb",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
     min_sdk_version: "Tiramisu",
 }
@@ -67,9 +64,7 @@
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.compos",
-        "com.android.uwb",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
     min_sdk_version: "Tiramisu",
 }
@@ -94,10 +89,7 @@
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.compos",
-        "com.android.rkpd",
-        "com.android.uwb",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
     min_sdk_version: "Tiramisu",
     lints: "none",
@@ -153,10 +145,7 @@
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.compos",
-        "com.android.rkpd",
-        "com.android.uwb",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
     min_sdk_version: "Tiramisu",
 }
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 2d1175b..f5b0071 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -114,7 +114,6 @@
     crate_name: "binder_rs_serialization_bindgen",
     wrapper_src: "serialization.hpp",
     source_stem: "bindings",
-    cpp_std: "gnu++17",
     bindgen_flags: [
         "--allowlist-type", "Transaction",
         "--allowlist-var", "TESTDATA_.*",
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index bd24a20..4c7684c 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -25,6 +25,7 @@
 
 cc_defaults {
     name: "binder_test_defaults",
+    cmake_snapshot_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -62,6 +63,7 @@
         "binderStatusUnitTest.cpp",
         "binderMemoryHeapBaseUnitTest.cpp",
         "binderRecordedTransactionTest.cpp",
+        "binderPersistableBundleTest.cpp",
     ],
     shared_libs: [
         "libbinder",
@@ -141,6 +143,7 @@
     name: "binderRpcTestIface",
     vendor_available: true,
     host_supported: true,
+    cmake_snapshot_supported: true,
     unstable: true,
     srcs: [
         "BinderRpcTestClientInfo.aidl",
@@ -222,6 +225,7 @@
 cc_defaults {
     name: "binderRpcTest_common_defaults",
     host_supported: true,
+    cmake_snapshot_supported: true,
     target: {
         darwin: {
             enabled: false,
@@ -263,6 +267,7 @@
     defaults: [
         "binderRpcTest_common_defaults",
     ],
+    compile_multilib: "first",
 
     srcs: [
         "binderRpcTest.cpp",
@@ -332,7 +337,7 @@
     ],
 }
 
-cc_test {
+cc_binary {
     // The module name cannot start with "binderRpcTest" because
     // then atest tries to execute it as part of binderRpcTest
     name: "binder_rpc_test_service",
@@ -343,7 +348,7 @@
     ],
 }
 
-cc_test {
+cc_binary {
     name: "binder_rpc_test_service_no_kernel",
     defaults: [
         "binderRpcTest_service_defaults",
@@ -354,7 +359,7 @@
     ],
 }
 
-cc_test {
+cc_binary {
     name: "binder_rpc_test_service_single_threaded",
     defaults: [
         "binderRpcTest_service_defaults",
@@ -369,7 +374,7 @@
     ],
 }
 
-cc_test {
+cc_binary {
     name: "binder_rpc_test_service_single_threaded_no_kernel",
     defaults: [
         "binderRpcTest_service_defaults",
@@ -381,6 +386,9 @@
     static_libs: [
         "libbinder_rpc_single_threaded_no_kernel",
     ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
 }
 
 cc_binary {
@@ -461,6 +469,20 @@
 }
 
 cc_test {
+    name: "binderRpcTestNoKernelAtAll",
+    defaults: [
+        "binderRpcTest_defaults",
+        "binderRpcTest_static_defaults",
+    ],
+    static_libs: [
+        "libbinder_rpc_no_kernel",
+    ],
+    cflags: [
+        "-DBINDER_NO_KERNEL_IPC_TESTING",
+    ],
+}
+
+cc_test {
     name: "binderRpcTestSingleThreaded",
     defaults: [
         "binderRpcTest_defaults",
@@ -487,6 +509,9 @@
     static_libs: [
         "libbinder_rpc_single_threaded_no_kernel",
     ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
 }
 
 cc_test {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 9788d9d..00406ed 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -38,6 +38,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <binder/Status.h>
 #include <binder/unique_fd.h>
 #include <utils/Flattenable.h>
 
@@ -57,6 +58,7 @@
 using namespace std::chrono_literals;
 using android::base::testing::HasValue;
 using android::base::testing::Ok;
+using android::binder::Status;
 using android::binder::unique_fd;
 using testing::ExplainMatchResult;
 using testing::Matcher;
@@ -253,7 +255,7 @@
     public:
         virtual void SetUp() {
             m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
-            IPCThreadState::self()->restoreCallingWorkSource(0); 
+            IPCThreadState::self()->restoreCallingWorkSource(0);
         }
         virtual void TearDown() {
         }
@@ -461,6 +463,35 @@
     EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder));
 }
 
+TEST_F(BinderLibTest, RegisterForNotificationsFailure) {
+    auto sm = defaultServiceManager();
+    using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
+    class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
+        void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
+        virtual ~LocalRegistrationCallbackImpl() {}
+    };
+    sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+
+    EXPECT_EQ(BAD_VALUE, sm->registerForNotifications(String16("ValidName"), nullptr));
+    EXPECT_EQ(UNKNOWN_ERROR, sm->registerForNotifications(String16("InvalidName!$"), cb));
+}
+
+TEST_F(BinderLibTest, UnregisterForNotificationsFailure) {
+    auto sm = defaultServiceManager();
+    using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
+    class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
+        void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
+        virtual ~LocalRegistrationCallbackImpl() {}
+    };
+    sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+
+    EXPECT_EQ(OK, sm->registerForNotifications(String16("ValidName"), cb));
+
+    EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("ValidName"), nullptr));
+    EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("AnotherValidName"), cb));
+    EXPECT_EQ(BAD_VALUE, sm->unregisterForNotifications(String16("InvalidName!!!"), cb));
+}
+
 TEST_F(BinderLibTest, WasParceled) {
     auto binder = sp<BBinder>::make();
     EXPECT_FALSE(binder->wasParceled());
diff --git a/libs/binder/tests/binderPersistableBundleTest.cpp b/libs/binder/tests/binderPersistableBundleTest.cpp
new file mode 100644
index 0000000..392e018
--- /dev/null
+++ b/libs/binder/tests/binderPersistableBundleTest.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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/Parcel.h>
+#include <binder/PersistableBundle.h>
+#include <gtest/gtest.h>
+#include <numeric>
+
+using android::OK;
+using android::Parcel;
+using android::status_t;
+using android::String16;
+using android::String8;
+using android::os::PersistableBundle;
+
+namespace android {
+
+inline std::string to_string(String16 const& str) {
+    return String8{str}.c_str();
+}
+
+namespace os {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out, std::vector<T> const& vec) {
+    using std::to_string;
+    auto str =
+            std::accumulate(vec.begin(), vec.end(), std::string{},
+                            [](std::string const& a, auto const& b) { return a + to_string(b); });
+    return out << str;
+}
+
+inline std::ostream& operator<<(std::ostream& out, PersistableBundle const& pb) {
+#define PRINT(TYPENAME, TYPE)                                \
+    for (auto const& key : pb.get##TYPENAME##Keys()) {       \
+        TYPE val{};                                          \
+        pb.get##TYPENAME(key, &val);                         \
+        out << #TYPE " " << key << ": " << val << std::endl; \
+    }
+
+    out << "size: " << pb.size() << std::endl;
+    PRINT(Boolean, bool);
+    PRINT(Int, int);
+    PRINT(Long, int64_t);
+    PRINT(Double, double);
+    PRINT(String, String16);
+    PRINT(BooleanVector, std::vector<bool>);
+    PRINT(IntVector, std::vector<int32_t>);
+    PRINT(LongVector, std::vector<int64_t>);
+    PRINT(DoubleVector, std::vector<double>);
+    PRINT(StringVector, std::vector<String16>);
+    PRINT(PersistableBundle, PersistableBundle);
+
+#undef PRINT
+
+    return out;
+}
+
+} // namespace os
+} // namespace android
+
+static const String16 kKey{"key"};
+
+static PersistableBundle createSimplePersistableBundle() {
+    PersistableBundle pb{};
+    pb.putInt(kKey, 64);
+    return pb;
+}
+
+#define TEST_PUT_AND_GET(TYPENAME, TYPE, ...)              \
+    TEST(PersistableBundle, PutAndGet##TYPENAME) {         \
+        TYPE const expected{__VA_ARGS__};                  \
+        PersistableBundle pb{};                            \
+                                                           \
+        pb.put##TYPENAME(kKey, expected);                  \
+                                                           \
+        std::set<String16> expectedKeys{kKey};             \
+        EXPECT_EQ(pb.get##TYPENAME##Keys(), expectedKeys); \
+                                                           \
+        TYPE val{};                                        \
+        EXPECT_TRUE(pb.get##TYPENAME(kKey, &val));         \
+        EXPECT_EQ(val, expected);                          \
+    }
+
+TEST_PUT_AND_GET(Boolean, bool, true);
+TEST_PUT_AND_GET(Int, int, 64);
+TEST_PUT_AND_GET(Long, int64_t, 42);
+TEST_PUT_AND_GET(Double, double, 42.64);
+TEST_PUT_AND_GET(String, String16, String16{"foo"});
+TEST_PUT_AND_GET(BooleanVector, std::vector<bool>, true, true);
+TEST_PUT_AND_GET(IntVector, std::vector<int32_t>, 1, 2);
+TEST_PUT_AND_GET(LongVector, std::vector<int64_t>, 1, 2);
+TEST_PUT_AND_GET(DoubleVector, std::vector<double>, 4.2, 5.9);
+TEST_PUT_AND_GET(StringVector, std::vector<String16>, String16{"foo"}, String16{"bar"});
+TEST_PUT_AND_GET(PersistableBundle, PersistableBundle, createSimplePersistableBundle());
+
+TEST(PersistableBundle, ParcelAndUnparcel) {
+    PersistableBundle expected = createSimplePersistableBundle();
+    PersistableBundle out{};
+
+    Parcel p{};
+    EXPECT_EQ(expected.writeToParcel(&p), 0);
+    p.setDataPosition(0);
+    EXPECT_EQ(out.readFromParcel(&p), 0);
+
+    EXPECT_EQ(expected, out);
+}
+
+TEST(PersistableBundle, OverwriteKey) {
+    PersistableBundle pb{};
+
+    pb.putInt(kKey, 64);
+    pb.putDouble(kKey, 0.5);
+
+    EXPECT_EQ(pb.getIntKeys().size(), 0);
+    EXPECT_EQ(pb.getDoubleKeys().size(), 1);
+
+    double out;
+    EXPECT_TRUE(pb.getDouble(kKey, &out));
+    EXPECT_EQ(out, 0.5);
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index c044d39..19882ea 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1278,7 +1278,7 @@
                 for (const auto& clientVersion : testVersions()) {
                     for (const auto& serverVersion : testVersions()) {
                         for (bool singleThreaded : {false, true}) {
-                            for (bool noKernel : {false, true}) {
+                            for (bool noKernel : noKernelValues()) {
                                 ret.push_back(BinderRpc::ParamType{
                                         .type = type,
                                         .security = security,
@@ -1299,7 +1299,7 @@
                     .clientVersion = RPC_WIRE_PROTOCOL_VERSION,
                     .serverVersion = RPC_WIRE_PROTOCOL_VERSION,
                     .singleThreaded = false,
-                    .noKernel = false,
+                    .noKernel = !kEnableKernelIpcTesting,
             });
         }
     }
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index acc0373..dc22647 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -64,6 +64,12 @@
 
 namespace android {
 
+#ifdef BINDER_NO_KERNEL_IPC_TESTING
+constexpr bool kEnableKernelIpcTesting = false;
+#else
+constexpr bool kEnableKernelIpcTesting = true;
+#endif
+
 constexpr char kLocalInetAddress[] = "127.0.0.1";
 
 enum class RpcSecurity { RAW, TLS };
@@ -72,6 +78,14 @@
     return {RpcSecurity::RAW, RpcSecurity::TLS};
 }
 
+static inline std::vector<bool> noKernelValues() {
+    std::vector<bool> values = {true};
+    if (kEnableKernelIpcTesting) {
+        values.push_back(false);
+    }
+    return values;
+}
+
 static inline bool hasExperimentalRpc() {
 #ifdef BINDER_RPC_TO_TRUSTY_TEST
     // Trusty services do not support the experimental version,
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index e59dc82..91145f0 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
 #include <binder/Parcel.h>
 #include <binder/RpcSession.h>
 #include <binder/Status.h>
 #include <gtest/gtest.h>
 
+#ifdef __ANDROID__
+#include <android-base/properties.h>
+#endif
+
 #include "../Debug.h"
 #include "../Utils.h"
 
@@ -69,8 +70,8 @@
     [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"a"))); },
     [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"baba"))); },
     [](Parcel* p) { ASSERT_EQ(OK, p->writeStrongBinder(nullptr)); },
-    [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(arraysize(kInt32Array), kInt32Array)); },
-    [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(arraysize(kByteArray), kByteArray)); },
+    [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(countof(kInt32Array), kInt32Array)); },
+    [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(countof(kByteArray), kByteArray)); },
     [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(true)); },
     [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(false)); },
     [](Parcel* p) { ASSERT_EQ(OK, p->writeChar('a')); },
@@ -162,8 +163,8 @@
 
 static void setParcelForRpc(Parcel* p, uint32_t version) {
     auto session = RpcSession::make();
-    CHECK(session->setProtocolVersion(version));
-    CHECK_EQ(OK, session->addNullDebuggingClient());
+    EXPECT_TRUE(session->setProtocolVersion(version));
+    EXPECT_EQ(OK, session->addNullDebuggingClient());
     p->markForRpc(session);
 }
 
@@ -180,13 +181,25 @@
     return result;
 }
 
+// To be replaced with std::views::split (and std::views::zip) once C++ compilers catch up.
+static std::vector<std::string> split(std::string_view s, char delimiter) {
+    std::vector<std::string> result;
+    size_t pos = 0;
+    while (true) {
+        const auto found = s.find(delimiter, pos);
+        result.emplace_back(s.substr(pos, found - pos));
+        if (found == s.npos) return result;
+        pos = found + 1;
+    }
+}
+
 static void checkRepr(const std::string& repr, uint32_t version) {
     const std::string actualRepr = buildRepr(version);
 
-    auto expected = base::Split(repr, "|");
+    auto expected = split(repr, '|');
     ASSERT_EQ(expected.size(), kFillFuns.size());
 
-    auto actual = base::Split(actualRepr, "|");
+    auto actual = split(actualRepr, '|');
     ASSERT_EQ(actual.size(), kFillFuns.size());
 
     for (size_t i = 0; i < kFillFuns.size(); i++) {
@@ -257,8 +270,13 @@
 
 TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) {
     if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
-        EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL")
-                << "Binder RPC wire protocol must be frozen on a release branch!";
+#ifdef __ANDROID__
+        bool isRelease = base::GetProperty("ro.build.version.codename", "") == "REL";
+#else
+        bool isRelease = true;
+#endif
+        EXPECT_FALSE(isRelease)
+                << "Binder RPC wire protocol must be frozen in release configuration!";
     }
 }
 
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index a8dabc3..157ab3c 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -21,6 +21,7 @@
 #include <lib/rand/rand.h>
 #endif
 
+#include <binder/Common.h>
 #include <binder/RpcTransportTipcTrusty.h>
 #include <log/log.h>
 #include <trusty_log.h>
@@ -39,6 +40,8 @@
 
 void trace_end(uint64_t) {}
 
+void trace_int(uint64_t, const char*, int32_t) {}
+
 uint64_t GetThreadId() {
     return 0;
 }
@@ -92,7 +95,8 @@
 
 } // namespace android::binder::os
 
-int __android_log_print(int prio [[maybe_unused]], const char* tag, const char* fmt, ...) {
+LIBBINDER_EXPORTED int __android_log_print(int prio [[maybe_unused]], const char* tag,
+                                           const char* fmt, ...) {
 #ifdef TRUSTY_USERSPACE
 #define trusty_tlog _tlog
 #define trusty_vtlog _vtlog
diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
index 8eae8c2..91c6008 100644
--- a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
@@ -26,7 +26,7 @@
 namespace android {
 
 // RpcTransportCtxFactory with TLS disabled.
-class RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory {
+class LIBBINDER_EXPORTED RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory {
 public:
     static std::unique_ptr<RpcTransportCtxFactory> make();
 
diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp
index 9b7f017..d5b1d7e 100644
--- a/libs/ftl/expected_test.cpp
+++ b/libs/ftl/expected_test.cpp
@@ -79,16 +79,28 @@
 
 namespace {
 
-IntExp increment(IntExp exp) {
+IntExp increment_try(IntExp exp) {
   const int i = FTL_TRY(exp);
   return IntExp(i + 1);
 }
 
-StringExp repeat(StringExp exp) {
+std::errc increment_expect(IntExp exp, int& out) {
+  const int i = FTL_EXPECT(exp);
+  out = i + 1;
+  return std::errc::operation_in_progress;
+}
+
+StringExp repeat_try(StringExp exp) {
   const std::string str = FTL_TRY(exp);
   return StringExp(str + str);
 }
 
+std::errc repeat_expect(StringExp exp, std::string& out) {
+  const std::string str = FTL_EXPECT(exp);
+  out = str + str;
+  return std::errc::operation_in_progress;
+}
+
 void uppercase(char& c, ftl::Optional<char> opt) {
   c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
 }
@@ -97,13 +109,13 @@
 
 // Keep in sync with example usage in header file.
 TEST(Expected, Try) {
-  EXPECT_EQ(IntExp(100), increment(IntExp(99)));
-  EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
+  EXPECT_EQ(IntExp(100), increment_try(IntExp(99)));
+  EXPECT_TRUE(increment_try(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
     return e == std::errc::value_too_large;
   }));
 
-  EXPECT_EQ(StringExp("haha"s), repeat(StringExp("ha"s)));
-  EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+  EXPECT_EQ(StringExp("haha"s), repeat_try(StringExp("ha"s)));
+  EXPECT_TRUE(repeat_try(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
     return e == std::errc::bad_message;
   }));
 
@@ -115,4 +127,19 @@
   EXPECT_EQ(c, 'A');
 }
 
+TEST(Expected, Expect) {
+  int i = 0;
+  EXPECT_EQ(std::errc::operation_in_progress, increment_expect(IntExp(99), i));
+  EXPECT_EQ(100, i);
+  EXPECT_EQ(std::errc::value_too_large,
+            increment_expect(ftl::Unexpected(std::errc::value_too_large), i));
+  EXPECT_EQ(100, i);
+
+  std::string str;
+  EXPECT_EQ(std::errc::operation_in_progress, repeat_expect(StringExp("ha"s), str));
+  EXPECT_EQ("haha"s, str);
+  EXPECT_EQ(std::errc::bad_message, repeat_expect(ftl::Unexpected(std::errc::bad_message), str));
+  EXPECT_EQ("haha"s, str);
+}
+
 }  // namespace android::test
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8f44b3a..d782f42 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -81,10 +81,6 @@
     visibility: ["//frameworks/native/services/inputflinger"],
     wrapper_src: "InputWrapper.hpp",
 
-    include_dirs: [
-        "frameworks/native/include",
-    ],
-
     source_stem: "bindings",
 
     bindgen_flags: [
@@ -117,6 +113,27 @@
         "--allowlist-var=AINPUT_SOURCE_HDMI",
         "--allowlist-var=AINPUT_SOURCE_SENSOR",
         "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
+        "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE",
+        "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC",
+        "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC",
+        "--allowlist-var=AMETA_NONE",
+        "--allowlist-var=AMETA_ALT_ON",
+        "--allowlist-var=AMETA_ALT_LEFT_ON",
+        "--allowlist-var=AMETA_ALT_RIGHT_ON",
+        "--allowlist-var=AMETA_SHIFT_ON",
+        "--allowlist-var=AMETA_SHIFT_LEFT_ON",
+        "--allowlist-var=AMETA_SHIFT_RIGHT_ON",
+        "--allowlist-var=AMETA_SYM_ON",
+        "--allowlist-var=AMETA_FUNCTION_ON",
+        "--allowlist-var=AMETA_CTRL_ON",
+        "--allowlist-var=AMETA_CTRL_LEFT_ON",
+        "--allowlist-var=AMETA_CTRL_RIGHT_ON",
+        "--allowlist-var=AMETA_META_ON",
+        "--allowlist-var=AMETA_META_LEFT_ON",
+        "--allowlist-var=AMETA_META_RIGHT_ON",
+        "--allowlist-var=AMETA_CAPS_LOCK_ON",
+        "--allowlist-var=AMETA_NUM_LOCK_ON",
+        "--allowlist-var=AMETA_SCROLL_LOCK_ON",
     ],
 
     static_libs: [
@@ -208,6 +225,7 @@
         "InputVerifier.cpp",
         "Keyboard.cpp",
         "KeyCharacterMap.cpp",
+        "KeyboardClassifier.cpp",
         "KeyLayoutMap.cpp",
         "MotionPredictor.cpp",
         "MotionPredictorMetricsManager.cpp",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index ee121d5..b098147 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -96,6 +96,19 @@
     return AMOTION_EVENT_ACTION_DOWN;
 }
 
+float transformOrientation(const ui::Transform& transform, const PointerCoords& coords,
+                           int32_t motionEventFlags) {
+    if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) == 0) {
+        return 0;
+    }
+
+    const bool isDirectionalAngle =
+            (motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION) != 0;
+
+    return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+                          isDirectionalAngle);
+}
+
 } // namespace
 
 const char* motionClassificationToString(MotionClassification classification) {
@@ -187,7 +200,7 @@
     return roundTransformedCoords(transformedXy - transformedOrigin);
 }
 
-float transformAngle(const ui::Transform& transform, float angleRadians) {
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional) {
     // Construct and transform a vector oriented at the specified clockwise angle from vertical.
     // Coordinate system: down is increasing Y, right is increasing X.
     float x = sinf(angleRadians);
@@ -201,6 +214,11 @@
     transformedPoint.x -= origin.x;
     transformedPoint.y -= origin.y;
 
+    if (!isDirectional && transformedPoint.y > 0) {
+        // Limit the range of atan2f to [-pi/2, pi/2] by reversing the direction of the vector.
+        transformedPoint *= -1;
+    }
+
     // Derive the transformed vector's clockwise angle from vertical.
     // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API.
     return atan2f(transformedPoint.x, -transformedPoint.y);
@@ -530,26 +548,6 @@
     return true;
 }
 
-void PointerCoords::transform(const ui::Transform& transform) {
-    const vec2 xy = transform.transform(getXYValue());
-    setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
-    setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
-
-    if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) ||
-        BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) {
-        const ui::Transform rotation(transform.getOrientation());
-        const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
-                                                   getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
-        setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
-        setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
-    }
-
-    if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) {
-        const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-        setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val));
-    }
-}
-
 // --- MotionEvent ---
 
 void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source,
@@ -723,13 +721,13 @@
 float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
                                              size_t historicalIndex) const {
     const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-    return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
+    return calculateTransformedAxisValue(axis, mSource, mFlags, mRawTransform, coords);
 }
 
 float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
                                           size_t historicalIndex) const {
     const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-    return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
+    return calculateTransformedAxisValue(axis, mSource, mFlags, mTransform, coords);
 }
 
 ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -786,8 +784,9 @@
     transform.set(matrix);
 
     // Apply the transformation to all samples.
-    std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
-                  [&transform](PointerCoords& c) { c.transform(transform); });
+    std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), [&](PointerCoords& c) {
+        calculateTransformedCoordsInPlace(c, mSource, mFlags, transform);
+    });
 
     if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
         mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
@@ -1059,7 +1058,7 @@
 }
 
 // Keep in sync with calculateTransformedCoords.
-float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
                                                  const ui::Transform& transform,
                                                  const PointerCoords& coords) {
     if (shouldDisregardTransformation(source)) {
@@ -1081,7 +1080,7 @@
     }
 
     if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
-        return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        return transformOrientation(transform, coords, flags);
     }
 
     return coords.getAxisValue(axis);
@@ -1089,29 +1088,32 @@
 
 // Keep in sync with calculateTransformedAxisValue. This is an optimization of
 // calculateTransformedAxisValue for all PointerCoords axes.
-PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
-                                                      const ui::Transform& transform,
-                                                      const PointerCoords& coords) {
+void MotionEvent::calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source,
+                                                    int32_t flags, const ui::Transform& transform) {
     if (shouldDisregardTransformation(source)) {
-        return coords;
+        return;
     }
-    PointerCoords out = coords;
 
     const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
-    out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
-    out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
 
     const vec2 relativeXy =
             transformWithoutTranslation(transform,
                                         {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
                                          coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
-    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
-    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
 
-    out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
-                     transformAngle(transform,
-                                    coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)));
+    coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+                        transformOrientation(transform, coords, flags));
+}
 
+PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, int32_t flags,
+                                                      const ui::Transform& transform,
+                                                      const PointerCoords& coords) {
+    PointerCoords out = coords;
+    calculateTransformedCoordsInPlace(out, source, flags, transform);
     return out;
 }
 
diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp
index abc0392..fcf490d 100644
--- a/libs/input/InputConsumer.cpp
+++ b/libs/input/InputConsumer.cpp
@@ -181,7 +181,8 @@
 }
 
 bool shouldResampleTool(ToolType toolType) {
-    return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN;
+    return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+            toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
 }
 
 } // namespace
@@ -592,6 +593,11 @@
             ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id);
             return;
         }
+        if (!shouldResampleTool(event->getToolType(i))) {
+            ALOGD_IF(debugResampling(),
+                     "Not resampled, containing unsupported tool type at pointer %d", id);
+            return;
+        }
     }
 
     // Find the data to use for resampling.
@@ -639,10 +645,18 @@
     }
 
     if (current->eventTime == sampleTime) {
-        // Prevents having 2 events with identical times and coordinates.
+        ALOGD_IF(debugResampling(), "Not resampled, 2 events with identical times.");
         return;
     }
 
+    for (size_t i = 0; i < pointerCount; i++) {
+        uint32_t id = event->getPointerId(i);
+        if (!other->idBits.hasBit(id)) {
+            ALOGD_IF(debugResampling(), "Not resampled, the other doesn't have pointer id %d.", id);
+            return;
+        }
+    }
+
     // Resample touch coordinates.
     History oldLastResample;
     oldLastResample.initializeFrom(touchState.lastResample);
@@ -670,22 +684,16 @@
         const PointerCoords& currentCoords = current->getPointerById(id);
         resampledCoords = currentCoords;
         resampledCoords.isResampled = true;
-        if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
-            const PointerCoords& otherCoords = other->getPointerById(id);
-            resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
-                                         lerp(currentCoords.getX(), otherCoords.getX(), alpha));
-            resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
-                                         lerp(currentCoords.getY(), otherCoords.getY(), alpha));
-            ALOGD_IF(debugResampling(),
-                     "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
-                     "other (%0.3f, %0.3f), alpha %0.3f",
-                     id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
-                     currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
-        } else {
-            ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
-                     resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
-                     currentCoords.getY());
-        }
+        const PointerCoords& otherCoords = other->getPointerById(id);
+        resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
+                                     lerp(currentCoords.getX(), otherCoords.getX(), alpha));
+        resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
+                                     lerp(currentCoords.getY(), otherCoords.getY(), alpha));
+        ALOGD_IF(debugResampling(),
+                 "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
+                 "other (%0.3f, %0.3f), alpha %0.3f",
+                 id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+                 currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
     }
 
     event->addSample(sampleTime, touchState.lastResample.pointers);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index bc67810..9333ab8 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -273,10 +273,7 @@
 }
 
 void InputDeviceInfo::setKeyboardType(int32_t keyboardType) {
-    static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
-    static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    // There can be multiple subdevices with different keyboard types, set it to the highest type
-    mKeyboardType = std::max(mKeyboardType, keyboardType);
+    mKeyboardType = keyboardType;
 }
 
 void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) {
diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp
new file mode 100644
index 0000000..0c2c7be
--- /dev/null
+++ b/libs/input/KeyboardClassifier.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 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 "KeyboardClassifier"
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <ftl/flags.h>
+#include <input/KeyboardClassifier.h>
+
+#include "input_cxx_bridge.rs.h"
+
+namespace input_flags = com::android::input::flags;
+
+using android::input::RustInputDeviceIdentifier;
+
+namespace android {
+
+KeyboardClassifier::KeyboardClassifier() {
+    if (input_flags::enable_keyboard_classifier()) {
+        mRustClassifier = android::input::keyboardClassifier::create();
+    }
+}
+
+KeyboardType KeyboardClassifier::getKeyboardType(DeviceId deviceId) {
+    if (mRustClassifier) {
+        return static_cast<KeyboardType>(
+                android::input::keyboardClassifier::getKeyboardType(**mRustClassifier, deviceId));
+    } else {
+        auto it = mKeyboardTypeMap.find(deviceId);
+        if (it == mKeyboardTypeMap.end()) {
+            return KeyboardType::NONE;
+        }
+        return it->second;
+    }
+}
+
+// Copied from EventHub.h
+const uint32_t DEVICE_CLASS_KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD;
+const uint32_t DEVICE_CLASS_ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY;
+
+void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId,
+                                               const InputDeviceIdentifier& identifier,
+                                               uint32_t deviceClasses) {
+    if (mRustClassifier) {
+        RustInputDeviceIdentifier rustIdentifier;
+        rustIdentifier.name = identifier.name;
+        rustIdentifier.location = identifier.location;
+        rustIdentifier.unique_id = identifier.uniqueId;
+        rustIdentifier.bus = identifier.bus;
+        rustIdentifier.vendor = identifier.vendor;
+        rustIdentifier.product = identifier.product;
+        rustIdentifier.version = identifier.version;
+        rustIdentifier.descriptor = identifier.descriptor;
+        android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId,
+                                                                  rustIdentifier, deviceClasses);
+    } else {
+        bool isKeyboard = (deviceClasses & DEVICE_CLASS_KEYBOARD) != 0;
+        bool hasAlphabeticKey = (deviceClasses & DEVICE_CLASS_ALPHAKEY) != 0;
+        mKeyboardTypeMap.insert_or_assign(deviceId,
+                                          isKeyboard ? (hasAlphabeticKey
+                                                                ? KeyboardType::ALPHABETIC
+                                                                : KeyboardType::NON_ALPHABETIC)
+                                                     : KeyboardType::NONE);
+    }
+}
+
+void KeyboardClassifier::processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState) {
+    if (mRustClassifier &&
+        !android::input::keyboardClassifier::isFinalized(**mRustClassifier, deviceId)) {
+        android::input::keyboardClassifier::processKey(**mRustClassifier, deviceId, evdevCode,
+                                                       metaState);
+    }
+}
+
+} // namespace android
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 90ed2b7..a77dfa5 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -116,6 +116,31 @@
     const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40;
 
     /**
+     * This flag indicates that the event has a valid value for AXIS_ORIENTATION.
+     *
+     * This is a private flag that is not used in Java.
+     * @hide
+     */
+    const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80;
+
+    /**
+     * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine
+     * the direction in which the tool is pointing. The value of the orientation axis will be in
+     * the range [-pi, pi], which represents a full circle. This is usually supported by devices
+     * like styluses.
+     *
+     * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing
+     * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2],
+     * which represents half a circle. This is usually the case for devices like touchscreens and
+     * touchpads, for which it is difficult to tell which direction along the major axis of the
+     * touch ellipse the finger is pointing.
+     *
+     * This is a private flag that is not used in Java.
+     * @hide
+     */
+    const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100;
+
+    /**
      * The input event was generated or modified by accessibility service.
      * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
      * set of flags, including in input/Input.h and in android/input.h.
@@ -234,4 +259,157 @@
      * time to adjust to changes in direction.
      */
     const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
+
+
+    /*
+     * Input device class: Keyboard
+     * The input device is a keyboard or has buttons.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_KEYBOARD = 0x00000001;
+
+    /*
+     * Input device class: Alphakey
+     * The input device is an alpha-numeric keyboard (not just a dial pad).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_ALPHAKEY = 0x00000002;
+
+    /*
+     * Input device class: Touch
+     * The input device is a touchscreen or a touchpad (either single-touch or multi-touch).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_TOUCH = 0x00000004;
+
+    /*
+     * Input device class: Cursor
+     * The input device is a cursor device such as a trackball or mouse.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_CURSOR = 0x00000008;
+
+    /*
+     * Input device class: Multi-touch
+     * The input device is a multi-touch touchscreen or touchpad.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_TOUCH_MT = 0x00000010;
+
+    /*
+     * Input device class: Dpad
+     * The input device is a directional pad (implies keyboard, has DPAD keys).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_DPAD = 0x00000020;
+
+    /*
+     * Input device class: Gamepad
+     * The input device is a gamepad (implies keyboard, has BUTTON keys).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_GAMEPAD = 0x00000040;
+
+    /*
+     * Input device class: Switch
+     * The input device has switches.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_SWITCH = 0x00000080;
+
+    /*
+     * Input device class: Joystick
+     * The input device is a joystick (implies gamepad, has joystick absolute axes).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_JOYSTICK = 0x00000100;
+
+    /*
+     * Input device class: Vibrator
+     * The input device has a vibrator (supports FF_RUMBLE).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_VIBRATOR = 0x00000200;
+
+    /*
+     * Input device class: Mic
+     * The input device has a microphone.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_MIC = 0x00000400;
+
+    /*
+     * Input device class: External Stylus
+     * The input device is an external stylus (has data we want to fuse with touch data).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800;
+
+    /*
+     * Input device class: Rotary Encoder
+     * The input device has a rotary encoder.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_ROTARY_ENCODER = 0x00001000;
+
+    /*
+     * Input device class: Sensor
+     * The input device has a sensor like accelerometer, gyro, etc.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_SENSOR = 0x00002000;
+
+    /*
+     * Input device class: Battery
+     * The input device has a battery.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_BATTERY = 0x00004000;
+
+    /*
+     * Input device class: Light
+     * The input device has sysfs controllable lights.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_LIGHT = 0x00008000;
+
+    /*
+     * Input device class: Touchpad
+     * The input device is a touchpad, requiring an on-screen cursor.
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_TOUCHPAD = 0x00010000;
+
+    /*
+     * Input device class: Virtual
+     * The input device is virtual (not a real device, not part of UI configuration).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_VIRTUAL = 0x20000000;
+
+    /*
+     * Input device class: External
+     * The input device is external (not built-in).
+     *
+     * @hide
+     */
+    const int DEVICE_CLASS_EXTERNAL = 0x40000000;
 }
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 560166c..a2192cb 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -150,3 +150,10 @@
   description: "Hide touch and pointer indicators if a secure window is present on display"
   bug: "325252005"
 }
+
+flag {
+  name: "enable_keyboard_classifier"
+  namespace: "input"
+  description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
+  bug: "263559234"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index d0dbd6f..564d94d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -16,22 +16,26 @@
 
 //! Common definitions of the Android Input Framework in rust.
 
+use crate::ffi::RustInputDeviceIdentifier;
 use bitflags::bitflags;
-use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_CANCELED;
-use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
-use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+use inputconstants::aidl::android::os::IInputConstants;
 use std::fmt;
 
 /// The InputDevice ID.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 pub struct DeviceId(pub i32);
 
+/// The InputDevice equivalent for Rust inputflinger
+#[derive(Debug)]
+pub struct InputDevice {
+    /// InputDevice ID
+    pub device_id: DeviceId,
+    /// InputDevice unique identifier
+    pub identifier: RustInputDeviceIdentifier,
+    /// InputDevice classes (equivalent to EventHub InputDeviceClass)
+    pub classes: DeviceClass,
+}
+
 #[repr(u32)]
 pub enum SourceClass {
     None = input_bindgen::AINPUT_SOURCE_CLASS_NONE,
@@ -192,23 +196,28 @@
     #[derive(Debug)]
     pub struct MotionFlags: u32 {
         /// FLAG_WINDOW_IS_OBSCURED
-        const WINDOW_IS_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32;
+        const WINDOW_IS_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32;
         /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED
-        const WINDOW_IS_PARTIALLY_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32;
+        const WINDOW_IS_PARTIALLY_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32;
         /// FLAG_HOVER_EXIT_PENDING
-        const HOVER_EXIT_PENDING = MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
+        const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
         /// FLAG_IS_GENERATED_GESTURE
-        const IS_GENERATED_GESTURE = MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
+        const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
         /// FLAG_CANCELED
-        const CANCELED = INPUT_EVENT_FLAG_CANCELED as u32;
+        const CANCELED = IInputConstants::INPUT_EVENT_FLAG_CANCELED as u32;
         /// FLAG_NO_FOCUS_CHANGE
-        const NO_FOCUS_CHANGE = MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+        const NO_FOCUS_CHANGE = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+        /// PRIVATE_FLAG_SUPPORTS_ORIENTATION
+        const PRIVATE_SUPPORTS_ORIENTATION = IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION as u32;
+        /// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION
+        const PRIVATE_SUPPORTS_DIRECTIONAL_ORIENTATION =
+                IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION as u32;
         /// FLAG_IS_ACCESSIBILITY_EVENT
-        const IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
+        const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
         /// FLAG_TAINTED
-        const TAINTED = INPUT_EVENT_FLAG_TAINTED as u32;
+        const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32;
         /// FLAG_TARGET_ACCESSIBILITY_FOCUS
-        const TARGET_ACCESSIBILITY_FOCUS = MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
+        const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
     }
 }
 
@@ -220,6 +229,107 @@
     }
 }
 
+bitflags! {
+    /// Device class of the input device. These are duplicated from Eventhub.h
+    /// We need to make sure the two version remain in sync when adding new classes.
+    #[derive(Debug, PartialEq)]
+    pub struct DeviceClass: u32 {
+        /// The input device is a keyboard or has buttons
+        const Keyboard = IInputConstants::DEVICE_CLASS_KEYBOARD as u32;
+        /// The input device is an alpha-numeric keyboard (not just a dial pad)
+        const AlphabeticKey = IInputConstants::DEVICE_CLASS_ALPHAKEY as u32;
+        /// The input device is a touchscreen or a touchpad (either single-touch or multi-touch)
+        const Touch = IInputConstants::DEVICE_CLASS_TOUCH as u32;
+        /// The input device is a cursor device such as a trackball or mouse.
+        const Cursor = IInputConstants::DEVICE_CLASS_CURSOR as u32;
+        /// The input device is a multi-touch touchscreen or touchpad.
+        const MultiTouch = IInputConstants::DEVICE_CLASS_TOUCH_MT as u32;
+        /// The input device is a directional pad (implies keyboard, has DPAD keys).
+        const Dpad = IInputConstants::DEVICE_CLASS_DPAD as u32;
+        /// The input device is a gamepad (implies keyboard, has BUTTON keys).
+        const Gamepad = IInputConstants::DEVICE_CLASS_GAMEPAD as u32;
+        /// The input device has switches.
+        const Switch = IInputConstants::DEVICE_CLASS_SWITCH as u32;
+        /// The input device is a joystick (implies gamepad, has joystick absolute axes).
+        const Joystick = IInputConstants::DEVICE_CLASS_JOYSTICK as u32;
+        /// The input device has a vibrator (supports FF_RUMBLE).
+        const Vibrator = IInputConstants::DEVICE_CLASS_VIBRATOR as u32;
+        /// The input device has a microphone.
+        const Mic = IInputConstants::DEVICE_CLASS_MIC as u32;
+        /// The input device is an external stylus (has data we want to fuse with touch data).
+        const ExternalStylus = IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS as u32;
+        /// The input device has a rotary encoder
+        const RotaryEncoder = IInputConstants::DEVICE_CLASS_ROTARY_ENCODER as u32;
+        /// The input device has a sensor like accelerometer, gyro, etc
+        const Sensor = IInputConstants::DEVICE_CLASS_SENSOR as u32;
+        /// The input device has a battery
+        const Battery = IInputConstants::DEVICE_CLASS_BATTERY as u32;
+        /// The input device has sysfs controllable lights
+        const Light = IInputConstants::DEVICE_CLASS_LIGHT as u32;
+        /// The input device is a touchpad, requiring an on-screen cursor.
+        const Touchpad = IInputConstants::DEVICE_CLASS_TOUCHPAD as u32;
+        /// The input device is virtual (not a real device, not part of UI configuration).
+        const Virtual = IInputConstants::DEVICE_CLASS_VIRTUAL as u32;
+        /// The input device is external (not built-in).
+        const External = IInputConstants::DEVICE_CLASS_EXTERNAL as u32;
+    }
+}
+
+bitflags! {
+    /// Modifier state flags
+    #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
+    pub struct ModifierState: u32 {
+        /// No meta keys are pressed
+        const None = input_bindgen::AMETA_NONE;
+        /// This mask is used to check whether one of the ALT meta keys is pressed
+        const AltOn = input_bindgen::AMETA_ALT_ON;
+        /// This mask is used to check whether the left ALT meta key is pressed
+        const AltLeftOn = input_bindgen::AMETA_ALT_LEFT_ON;
+        /// This mask is used to check whether the right ALT meta key is pressed
+        const AltRightOn = input_bindgen::AMETA_ALT_RIGHT_ON;
+        /// This mask is used to check whether one of the SHIFT meta keys is pressed
+        const ShiftOn = input_bindgen::AMETA_SHIFT_ON;
+        /// This mask is used to check whether the left SHIFT meta key is pressed
+        const ShiftLeftOn = input_bindgen::AMETA_SHIFT_LEFT_ON;
+        /// This mask is used to check whether the right SHIFT meta key is pressed
+        const ShiftRightOn = input_bindgen::AMETA_SHIFT_RIGHT_ON;
+        /// This mask is used to check whether the SYM meta key is pressed
+        const SymOn = input_bindgen::AMETA_SYM_ON;
+        /// This mask is used to check whether the FUNCTION meta key is pressed
+        const FunctionOn = input_bindgen::AMETA_FUNCTION_ON;
+        /// This mask is used to check whether one of the CTRL meta keys is pressed
+        const CtrlOn = input_bindgen::AMETA_CTRL_ON;
+        /// This mask is used to check whether the left CTRL meta key is pressed
+        const CtrlLeftOn = input_bindgen::AMETA_CTRL_LEFT_ON;
+        /// This mask is used to check whether the right CTRL meta key is pressed
+        const CtrlRightOn = input_bindgen::AMETA_CTRL_RIGHT_ON;
+        /// This mask is used to check whether one of the META meta keys is pressed
+        const MetaOn = input_bindgen::AMETA_META_ON;
+        /// This mask is used to check whether the left META meta key is pressed
+        const MetaLeftOn = input_bindgen::AMETA_META_LEFT_ON;
+        /// This mask is used to check whether the right META meta key is pressed
+        const MetaRightOn = input_bindgen::AMETA_META_RIGHT_ON;
+        /// This mask is used to check whether the CAPS LOCK meta key is on
+        const CapsLockOn = input_bindgen::AMETA_CAPS_LOCK_ON;
+        /// This mask is used to check whether the NUM LOCK meta key is on
+        const NumLockOn = input_bindgen::AMETA_NUM_LOCK_ON;
+        /// This mask is used to check whether the SCROLL LOCK meta key is on
+        const ScrollLockOn = input_bindgen::AMETA_SCROLL_LOCK_ON;
+    }
+}
+
+/// A rust enum representation of a Keyboard type.
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum KeyboardType {
+    /// KEYBOARD_TYPE_NONE
+    None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE,
+    /// KEYBOARD_TYPE_NON_ALPHABETIC
+    NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+    /// KEYBOARD_TYPE_ALPHABETIC
+    Alphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+}
+
 #[cfg(test)]
 mod tests {
     use crate::input::SourceClass;
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
new file mode 100644
index 0000000..1063fac
--- /dev/null
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2024 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.
+ */
+
+//! Contains the KeyboardClassifier, that tries to identify whether an Input device is an
+//! alphabetic or non-alphabetic keyboard. It also tracks the KeyEvents produced by the device
+//! in order to verify/change the inferred keyboard type.
+//!
+//! Initial classification:
+//! - If DeviceClass includes Dpad, Touch, Cursor, MultiTouch, ExternalStylus, Touchpad, Dpad,
+//!   Gamepad, Switch, Joystick, RotaryEncoder => KeyboardType::NonAlphabetic
+//! - Otherwise if DeviceClass has Keyboard and not AlphabeticKey => KeyboardType::NonAlphabetic
+//! - Otherwise if DeviceClass has both Keyboard and AlphabeticKey => KeyboardType::Alphabetic
+//!
+//! On process keys:
+//! - If KeyboardType::NonAlphabetic and we receive alphabetic key event, then change type to
+//!   KeyboardType::Alphabetic. Once changed, no further changes. (i.e. verified = true)
+//! - TODO(b/263559234): If KeyboardType::Alphabetic and we don't receive any alphabetic key event
+//!    across multiple device connections in a time period, then change type to
+//!    KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic
+//!    (i.e. verified = false).
+//!
+//! TODO(b/263559234): Data store implementation to store information about past classification
+
+use crate::input::{DeviceId, InputDevice, KeyboardType};
+use crate::{DeviceClass, ModifierState};
+use std::collections::HashMap;
+
+/// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic
+/// keyboard or non-alphabetic keyboard
+#[derive(Default)]
+pub struct KeyboardClassifier {
+    device_map: HashMap<DeviceId, KeyboardInfo>,
+}
+
+struct KeyboardInfo {
+    _device: InputDevice,
+    keyboard_type: KeyboardType,
+    is_finalized: bool,
+}
+
+impl KeyboardClassifier {
+    /// Create a new KeyboardClassifier
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Adds keyboard to KeyboardClassifier
+    pub fn notify_keyboard_changed(&mut self, device: InputDevice) {
+        let (keyboard_type, is_finalized) = self.classify_keyboard(&device);
+        self.device_map.insert(
+            device.device_id,
+            KeyboardInfo { _device: device, keyboard_type, is_finalized },
+        );
+    }
+
+    /// Get keyboard type for a tracked keyboard in KeyboardClassifier
+    pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType {
+        return if let Some(keyboard) = self.device_map.get(&device_id) {
+            keyboard.keyboard_type
+        } else {
+            KeyboardType::None
+        };
+    }
+
+    /// Tells if keyboard type classification is finalized. Once finalized the classification can't
+    /// change until device is reconnected again.
+    ///
+    /// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or
+    /// allowlist that are explicitly categorized and won't change with future key events
+    pub fn is_finalized(&self, device_id: DeviceId) -> bool {
+        return if let Some(keyboard) = self.device_map.get(&device_id) {
+            keyboard.is_finalized
+        } else {
+            false
+        };
+    }
+
+    /// Process a key event and change keyboard type if required.
+    /// - If any key event occurs, the keyboard type will change from None to NonAlphabetic
+    /// - If an alphabetic key occurs, the keyboard type will change to Alphabetic
+    pub fn process_key(
+        &mut self,
+        device_id: DeviceId,
+        evdev_code: i32,
+        modifier_state: ModifierState,
+    ) {
+        if let Some(keyboard) = self.device_map.get_mut(&device_id) {
+            // Ignore all key events with modifier state since they can be macro shortcuts used by
+            // some non-keyboard peripherals like TV remotes, game controllers, etc.
+            if modifier_state.bits() != 0 {
+                return;
+            }
+            if Self::is_alphabetic_key(&evdev_code) {
+                keyboard.keyboard_type = KeyboardType::Alphabetic;
+                keyboard.is_finalized = true;
+            }
+        }
+    }
+
+    fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) {
+        // This should never happen but having keyboard device class is necessary to be classified
+        // as any type of keyboard.
+        if !device.classes.contains(DeviceClass::Keyboard) {
+            return (KeyboardType::None, true);
+        }
+        // Normal classification for internal and virtual keyboards
+        if !device.classes.contains(DeviceClass::External)
+            || device.classes.contains(DeviceClass::Virtual)
+        {
+            return if device.classes.contains(DeviceClass::AlphabeticKey) {
+                (KeyboardType::Alphabetic, true)
+            } else {
+                (KeyboardType::NonAlphabetic, true)
+            };
+        }
+        // Any composite device with multiple device classes should be categorized as non-alphabetic
+        // keyboard initially
+        if device.classes.contains(DeviceClass::Touch)
+            || device.classes.contains(DeviceClass::Cursor)
+            || device.classes.contains(DeviceClass::MultiTouch)
+            || device.classes.contains(DeviceClass::ExternalStylus)
+            || device.classes.contains(DeviceClass::Touchpad)
+            || device.classes.contains(DeviceClass::Dpad)
+            || device.classes.contains(DeviceClass::Gamepad)
+            || device.classes.contains(DeviceClass::Switch)
+            || device.classes.contains(DeviceClass::Joystick)
+            || device.classes.contains(DeviceClass::RotaryEncoder)
+        {
+            // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the
+            // kernel, we no longer need to process key events to verify.
+            return (
+                KeyboardType::NonAlphabetic,
+                !device.classes.contains(DeviceClass::AlphabeticKey),
+            );
+        }
+        // Only devices with "Keyboard" and "AlphabeticKey" should be classified as full keyboard
+        if device.classes.contains(DeviceClass::AlphabeticKey) {
+            (KeyboardType::Alphabetic, true)
+        } else {
+            // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the
+            // kernel, we no longer need to process key events to verify.
+            (KeyboardType::NonAlphabetic, true)
+        }
+    }
+
+    fn is_alphabetic_key(evdev_code: &i32) -> bool {
+        // Keyboard alphabetic row 1 (Q W E R T Y U I O P [ ])
+        (16..=27).contains(evdev_code)
+            // Keyboard alphabetic row 2 (A S D F G H J K L ; ' `)
+            || (30..=41).contains(evdev_code)
+            // Keyboard alphabetic row 3 (\ Z X C V B N M , . /)
+            || (43..=53).contains(evdev_code)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::input::{DeviceId, InputDevice, KeyboardType};
+    use crate::keyboard_classifier::KeyboardClassifier;
+    use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier};
+
+    static DEVICE_ID: DeviceId = DeviceId(1);
+    static KEY_A: i32 = 30;
+    static KEY_1: i32 = 2;
+
+    #[test]
+    fn classify_external_alphabetic_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic);
+        assert!(classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_external_non_alphabetic_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier
+            .notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_mouse_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Cursor
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_touchpad_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Touchpad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_stylus_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::ExternalStylus
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_dpad_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Dpad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_joystick_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Joystick
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn classify_gamepad_pretending_as_keyboard() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Gamepad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn reclassify_keyboard_on_alphabetic_key_event() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Dpad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+
+        // on alphabetic key event
+        classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None);
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic);
+        assert!(classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn dont_reclassify_keyboard_on_non_alphabetic_key_event() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Dpad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+
+        // on number key event
+        classifier.process_key(DEVICE_ID, KEY_1, ModifierState::None);
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    #[test]
+    fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() {
+        let mut classifier = KeyboardClassifier::new();
+        classifier.notify_keyboard_changed(create_device(
+            DeviceClass::Keyboard
+                | DeviceClass::Dpad
+                | DeviceClass::AlphabeticKey
+                | DeviceClass::External,
+        ));
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+
+        classifier.process_key(DEVICE_ID, KEY_A, ModifierState::CtrlOn);
+        assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+        assert!(!classifier.is_finalized(DEVICE_ID));
+    }
+
+    fn create_device(classes: DeviceClass) -> InputDevice {
+        InputDevice {
+            device_id: DEVICE_ID,
+            identifier: RustInputDeviceIdentifier {
+                name: "test_device".to_string(),
+                location: "location".to_string(),
+                unique_id: "unique_id".to_string(),
+                bus: 123,
+                vendor: 234,
+                product: 345,
+                version: 567,
+                descriptor: "descriptor".to_string(),
+            },
+            classes,
+        }
+    }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index fb3f520..5010475 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -18,9 +18,13 @@
 
 mod input;
 mod input_verifier;
+mod keyboard_classifier;
 
-pub use input::{DeviceId, MotionAction, MotionFlags, Source};
+pub use input::{
+    DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source,
+};
 pub use input_verifier::InputVerifier;
+pub use keyboard_classifier::KeyboardClassifier;
 
 #[cxx::bridge(namespace = "android::input")]
 #[allow(unsafe_op_in_unsafe_fn)]
@@ -47,7 +51,8 @@
         /// }
         /// ```
         type InputVerifier;
-        fn create(name: String) -> Box<InputVerifier>;
+        #[cxx_name = create]
+        fn create_input_verifier(name: String) -> Box<InputVerifier>;
         fn process_movement(
             verifier: &mut InputVerifier,
             device_id: i32,
@@ -59,15 +64,53 @@
         fn reset_device(verifier: &mut InputVerifier, device_id: i32);
     }
 
+    #[namespace = "android::input::keyboardClassifier"]
+    extern "Rust" {
+        /// Used to classify a keyboard into alphabetic and non-alphabetic
+        type KeyboardClassifier;
+        #[cxx_name = create]
+        fn create_keyboard_classifier() -> Box<KeyboardClassifier>;
+        #[cxx_name = notifyKeyboardChanged]
+        fn notify_keyboard_changed(
+            classifier: &mut KeyboardClassifier,
+            device_id: i32,
+            identifier: RustInputDeviceIdentifier,
+            device_classes: u32,
+        );
+        #[cxx_name = getKeyboardType]
+        fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32;
+        #[cxx_name = isFinalized]
+        fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool;
+        #[cxx_name = processKey]
+        fn process_key(
+            classifier: &mut KeyboardClassifier,
+            device_id: i32,
+            evdev_code: i32,
+            modifier_state: u32,
+        );
+    }
+
     #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
     pub struct RustPointerProperties {
         pub id: i32,
     }
+
+    #[derive(Debug)]
+    pub struct RustInputDeviceIdentifier {
+        pub name: String,
+        pub location: String,
+        pub unique_id: String,
+        pub bus: u16,
+        pub vendor: u16,
+        pub product: u16,
+        pub version: u16,
+        pub descriptor: String,
+    }
 }
 
-use crate::ffi::RustPointerProperties;
+use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties};
 
-fn create(name: String) -> Box<InputVerifier> {
+fn create_input_verifier(name: String) -> Box<InputVerifier> {
     Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
 }
 
@@ -103,3 +146,53 @@
 fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
     verifier.reset_device(DeviceId(device_id));
 }
+
+fn create_keyboard_classifier() -> Box<KeyboardClassifier> {
+    Box::new(KeyboardClassifier::new())
+}
+
+fn notify_keyboard_changed(
+    classifier: &mut KeyboardClassifier,
+    device_id: i32,
+    identifier: RustInputDeviceIdentifier,
+    device_classes: u32,
+) {
+    let classes = DeviceClass::from_bits(device_classes);
+    if classes.is_none() {
+        panic!(
+            "The conversion of device class 0x{:08x} failed, please check if some device classes
+             have not been added to DeviceClass.",
+            device_classes
+        );
+    }
+    classifier.notify_keyboard_changed(InputDevice {
+        device_id: DeviceId(device_id),
+        identifier,
+        classes: classes.unwrap(),
+    });
+}
+
+fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32 {
+    classifier.get_keyboard_type(DeviceId(device_id)) as u32
+}
+
+fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool {
+    classifier.is_finalized(DeviceId(device_id))
+}
+
+fn process_key(
+    classifier: &mut KeyboardClassifier,
+    device_id: i32,
+    evdev_code: i32,
+    meta_state: u32,
+) {
+    let modifier_state = ModifierState::from_bits(meta_state);
+    if modifier_state.is_none() {
+        panic!(
+            "The conversion of meta state 0x{:08x} failed, please check if some meta state
+             have not been added to ModifierState.",
+            meta_state
+        );
+    }
+    classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
+}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 476b5cf..3717f49 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,23 +26,39 @@
 
 namespace android {
 
+namespace {
+
 // Default display id.
-static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
 
-static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
 
-static constexpr auto POINTER_0_DOWN =
+constexpr auto POINTER_0_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-static constexpr auto POINTER_1_DOWN =
+constexpr auto POINTER_1_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-static constexpr auto POINTER_0_UP =
+constexpr auto POINTER_0_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-static constexpr auto POINTER_1_UP =
+constexpr auto POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
+std::array<float, 9> asFloat9(const ui::Transform& t) {
+    std::array<float, 9> mat{};
+    mat[0] = t[0][0];
+    mat[1] = t[1][0];
+    mat[2] = t[2][0];
+    mat[3] = t[0][1];
+    mat[4] = t[1][1];
+    mat[5] = t[2][1];
+    mat[6] = t[0][2];
+    mat[7] = t[1][2];
+    mat[8] = t[2][2];
+    return mat;
+}
+
 class BaseTest : public testing::Test {
 protected:
     static constexpr std::array<uint8_t, 32> HMAC = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
@@ -50,6 +66,8 @@
                                                      22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
 };
 
+} // namespace
+
 // --- PointerCoordsTest ---
 
 class PointerCoordsTest : public BaseTest {
@@ -344,13 +362,15 @@
 }
 
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+    const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
     event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
-                      AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
-                      AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
-                      MotionClassification::NONE, mTransform, 2.0f, 2.1f,
-                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
-                      mPointerProperties, mSamples[0].pointerCoords);
+                      AMOTION_EVENT_ACTION_MOVE, 0, flags, AMOTION_EVENT_EDGE_FLAG_TOP,
+                      AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
+                      mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                      AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME,
+                      ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords);
     event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
     event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
 }
@@ -364,7 +384,10 @@
     ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
     EXPECT_EQ(HMAC, event->getHmac());
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
-    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
+    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+                      AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+                      AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+              event->getFlags());
     ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
     ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
@@ -799,8 +822,10 @@
     }
     MotionEvent event;
     ui::Transform identityTransform;
+    const int32_t flags = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
     event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
-                     INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0,
+                     INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, flags,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
                      MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
                      /*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2,
@@ -1087,4 +1112,90 @@
     ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
 }
 
+TEST_F(MotionEventTest, InvalidOrientationNotRotated) {
+    // This touch event does not have a value for AXIS_ORIENTATION, and the flags are implicitly
+    // set to 0. The transform is set to a 90-degree rotation.
+    MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                .downTime(ARBITRARY_DOWN_TIME)
+                                .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+                                .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+                                .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+                                .build();
+    ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+    event.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+    ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+    event.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+    ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+    event.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+    ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+}
+
+TEST_F(MotionEventTest, ValidZeroOrientationRotated) {
+    // This touch events will implicitly have a value of 0 for its AXIS_ORIENTATION.
+    auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                           .downTime(ARBITRARY_DOWN_TIME)
+                           .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+                           .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+                           .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+                           .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+    MotionEvent nonDirectionalEvent = builder.build();
+    MotionEvent directionalEvent =
+            builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+    // The angle is rotated by the initial transform, a 90-degree rotation.
+    ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), M_PI_2, EPSILON);
+
+    nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+    directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+    ASSERT_NEAR(fabs(directionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI, EPSILON);
+
+    nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+    directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+
+    nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+    directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+    ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), -M_PI_2, EPSILON);
+}
+
+TEST_F(MotionEventTest, ValidNonZeroOrientationRotated) {
+    const float initial = 1.f;
+    auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                           .downTime(ARBITRARY_DOWN_TIME)
+                           .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER)
+                                            .x(4)
+                                            .y(4)
+                                            .axis(AMOTION_EVENT_AXIS_ORIENTATION, initial))
+                           .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+                           .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+                           .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+
+    MotionEvent nonDirectionalEvent = builder.build();
+    MotionEvent directionalEvent =
+            builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+    // The angle is rotated by the initial transform, a 90-degree rotation.
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial + M_PI_2, EPSILON);
+
+    nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+    directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI, EPSILON);
+
+    nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+    directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+
+    nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+    directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+    ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+    ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 70529bb..f49469c 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -96,7 +96,9 @@
     hmac = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
             16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
 
-    flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+    flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
     if (action == AMOTION_EVENT_ACTION_CANCEL) {
         flags |= AMOTION_EVENT_FLAG_CANCELED;
     }
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 48512f7..e65a919 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -89,7 +89,9 @@
     hmac = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
             16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
 
-    flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+    flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+            AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
     if (action == AMOTION_EVENT_ACTION_CANCEL) {
         flags |= AMOTION_EVENT_FLAG_CANCELED;
     }
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 2dc9fdb..8d8b530 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -297,10 +297,9 @@
 }
 
 /**
- * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
- * a resampled timestamp and should be marked as such.
+ * Stylus pointer coordinates are resampled.
  */
-TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
+TEST_F(TouchResamplingTest, StylusEventIsResampled) {
     std::chrono::nanoseconds frameTime;
     std::vector<InputEventEntry> entries, expectedEntries;
 
@@ -330,15 +329,91 @@
             //      id  x   y
             {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
             {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
-            // A resampled event is generated, but the stylus coordinates are not resampled.
             {25ms,
-             {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
+             {{0, 35, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
              AMOTION_EVENT_ACTION_MOVE},
     };
     consumeInputEventEntries(expectedEntries, frameTime);
 }
 
 /**
+ * Mouse pointer coordinates are resampled.
+ */
+TEST_F(TouchResamplingTest, MouseEventIsResampled) {
+    std::chrono::nanoseconds frameTime;
+    std::vector<InputEventEntry> entries, expectedEntries;
+
+    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // InputEvent with a single action.
+    entries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 5ms;
+    expectedEntries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    entries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 35ms;
+    expectedEntries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+            {25ms,
+             {{0, 35, 30, .toolType = ToolType::MOUSE, .isResampled = true}},
+             AMOTION_EVENT_ACTION_MOVE},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
+ * Motion events with palm tool type are not resampled.
+ */
+TEST_F(TouchResamplingTest, PalmEventIsNotResampled) {
+    std::chrono::nanoseconds frameTime;
+    std::vector<InputEventEntry> entries, expectedEntries;
+
+    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // InputEvent with a single action.
+    entries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 5ms;
+    expectedEntries = {
+            //      id  x   y
+            {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    entries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+    };
+    publishInputEventEntries(entries);
+    frameTime = 35ms;
+    expectedEntries = {
+            //      id  x   y
+            {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+            {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+    };
+    consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
  * Event should not be resampled when sample time is equal to event time.
  */
 TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 757d935..4a04467 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,6 +50,7 @@
         "libshaders",
         "libtonemap",
         "libsurfaceflinger_common",
+        "libsurfaceflingerflags",
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 1c60563..bc3976d 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -22,24 +22,49 @@
 #include "skia/SkiaGLRenderEngine.h"
 #include "threaded/RenderEngineThreaded.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \
+        COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE)
+#define COMPILE_GRAPHITE_RENDERENGINE 1
+#else
+#define COMPILE_GRAPHITE_RENDERENGINE 0
+#endif
+
 namespace android {
 namespace renderengine {
 
 std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
     threaded::CreateInstanceFactory createInstanceFactory;
 
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
+    const RenderEngine::SkiaBackend actualSkiaBackend = args.skiaBackend;
+#else
+    if (args.skiaBackend == RenderEngine::SkiaBackend::GRAPHITE) {
+        ALOGE("RenderEngine with Graphite Skia backend was requested, but Graphite was not "
+              "included in the build. Falling back to Ganesh (%s)",
+              args.graphicsApi == RenderEngine::GraphicsApi::GL ? "GL" : "Vulkan");
+    }
+    const RenderEngine::SkiaBackend actualSkiaBackend = RenderEngine::SkiaBackend::GANESH;
+#endif
+
     ALOGD("%sRenderEngine with %s Backend (%s)", args.threaded == Threaded::YES ? "Threaded " : "",
           args.graphicsApi == GraphicsApi::GL ? "SkiaGL" : "SkiaVK",
-          args.skiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite");
+          actualSkiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite");
 
-    if (args.skiaBackend == SkiaBackend::GRAPHITE) {
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
+    if (actualSkiaBackend == SkiaBackend::GRAPHITE) {
         createInstanceFactory = [args]() {
             return android::renderengine::skia::GraphiteVkRenderEngine::create(args);
         };
-    } else { // GANESH
+    } else
+#endif
+    { // GANESH
         if (args.graphicsApi == GraphicsApi::VK) {
             createInstanceFactory = [args]() {
                 return android::renderengine::skia::GaneshVkRenderEngine::create(args);
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
index fc16c56..5e756b0 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -197,7 +197,9 @@
     LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
 };
 
-static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
+static skgpu::VulkanGetProc sGetProc = [](const char* proc_name,
+                                          VkInstance instance,
+                                          VkDevice device) {
     if (device != VK_NULL_HANDLE) {
         return vkGetDeviceProcAddr(device, proc_name);
     }
@@ -604,7 +606,7 @@
     mQueue = VK_NULL_HANDLE;          // Implicitly destroyed by destroying mDevice.
     mQueueIndex = 0;
     mApiVersion = 0;
-    mGrExtensions = GrVkExtensions();
+    mGrExtensions = skgpu::VulkanExtensions();
     mGrGetProc = nullptr;
     mIsProtected = false;
     mIsRealtimePriority = false;
diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h
index af0489a..f20b002 100644
--- a/libs/renderengine/skia/VulkanInterface.h
+++ b/libs/renderengine/skia/VulkanInterface.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include <include/gpu/vk/GrVkBackendContext.h>
-#include <include/gpu/vk/GrVkExtensions.h>
+#include <include/gpu/vk/VulkanExtensions.h>
+#include <include/gpu/vk/VulkanTypes.h>
 
 #include <vulkan/vulkan.h>
 
@@ -85,12 +86,12 @@
     VkQueue mQueue = VK_NULL_HANDLE;
     int mQueueIndex = 0;
     uint32_t mApiVersion = 0;
-    GrVkExtensions mGrExtensions;
+    skgpu::VulkanExtensions mGrExtensions;
     VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr;
     VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr;
     VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr;
     VkPhysicalDeviceFaultFeaturesEXT* mDeviceFaultFeatures = nullptr;
-    GrVkGetProc mGrGetProc = nullptr;
+    skgpu::VulkanGetProc mGrGetProc = nullptr;
     bool mIsProtected = false;
     bool mIsRealtimePriority = false;
 
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 0eea187..0783714 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -46,6 +46,7 @@
         "libshaders",
         "libtonemap",
         "libsurfaceflinger_common",
+        "libsurfaceflingerflags",
     ],
     header_libs: [
         "libtonemap_headers",
@@ -64,5 +65,6 @@
         "libui",
         "libutils",
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
     ],
 }
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 4dcaff9..a8a9823 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,6 +22,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
@@ -41,6 +42,14 @@
 #include "../skia/SkiaVkRenderEngine.h"
 #include "../threaded/RenderEngineThreaded.h"
 
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \
+        COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE)
+#define COMPILE_GRAPHITE_RENDERENGINE 1
+#else
+#define COMPILE_GRAPHITE_RENDERENGINE 0
+#endif
+
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
 constexpr int DEFAULT_DISPLAY_OFFSET = 64;
@@ -152,6 +161,8 @@
     }
 };
 
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
 class GraphiteVkRenderEngineFactory : public RenderEngineFactory {
 public:
     std::string name() override { return "GraphiteVkRenderEngineFactory"; }
@@ -164,6 +175,7 @@
         return renderengine::RenderEngine::SkiaBackend::GRAPHITE;
     }
 };
+#endif
 
 class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
 public:
@@ -1497,10 +1509,15 @@
     expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
 }
 
+// TODO: b/341728634 - Clean up conditional compilation.
 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
                          testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
-                                         std::make_shared<GaneshVkRenderEngineFactory>(),
-                                         std::make_shared<GraphiteVkRenderEngineFactory>()));
+                                         std::make_shared<GaneshVkRenderEngineFactory>()
+#if COMPILE_GRAPHITE_RENDERENGINE
+                                                 ,
+                                         std::make_shared<GraphiteVkRenderEngineFactory>()
+#endif
+                                                 ));
 
 TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
     if (!GetParam()->apiSupported()) {
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
index c511f4a..cbf3055 100644
--- a/libs/sensor/libsensor_flags.aconfig
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -8,3 +8,10 @@
   bug: "322228259"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "sensor_event_queue_report_sensor_usage_in_tracing"
+  namespace: "sensors"
+  description: "When this flag is enabled, sensor event queue will report sensor usage when system trace is enabled."
+  bug: "333132224"
+}
\ No newline at end of file
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 3a31fa0..8a14db8 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,6 +20,7 @@
 #include <ostream>
 #include <string>
 
+#include <ftl/hash.h>
 #include <ftl/optional.h>
 
 namespace android {
@@ -38,6 +39,9 @@
     constexpr DisplayId(const DisplayId&) = default;
     DisplayId& operator=(const DisplayId&) = default;
 
+    constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
+    constexpr bool isStable() const { return value & FLAG_STABLE; }
+
     uint64_t value;
 
     // For deserialization.
@@ -76,10 +80,10 @@
 // DisplayId of a physical display, such as the internal display or externally connected display.
 struct PhysicalDisplayId : DisplayId {
     static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
-        if (id.value & FLAG_VIRTUAL) {
+        if (id.isVirtual()) {
             return std::nullopt;
         }
-        return {PhysicalDisplayId(id)};
+        return PhysicalDisplayId(id);
     }
 
     // Returns a stable ID based on EDID information.
@@ -117,25 +121,23 @@
     static constexpr uint64_t FLAG_GPU = 1ULL << 61;
 
     static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
-        if (id.value & FLAG_VIRTUAL) {
-            return {VirtualDisplayId(id)};
+        if (id.isVirtual()) {
+            return VirtualDisplayId(id);
         }
         return std::nullopt;
     }
 
 protected:
-    constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
-          : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
-
+    explicit constexpr VirtualDisplayId(uint64_t value) : DisplayId(FLAG_VIRTUAL | value) {}
     explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
 struct HalVirtualDisplayId : VirtualDisplayId {
-    explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+    explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {}
 
     static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
-        if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
-            return {HalVirtualDisplayId(id)};
+        if (id.isVirtual() && !(id.value & FLAG_GPU)) {
+            return HalVirtualDisplayId(id);
         }
         return std::nullopt;
     }
@@ -145,17 +147,27 @@
 };
 
 struct GpuVirtualDisplayId : VirtualDisplayId {
-    explicit constexpr GpuVirtualDisplayId(BaseId baseId)
-          : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+    explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
+
+    static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) {
+        if (const auto hashOpt = ftl::stable_hash(uniqueId)) {
+            return GpuVirtualDisplayId(HashTag{}, *hashOpt);
+        }
+        return {};
+    }
 
     static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
-        if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
-            return {GpuVirtualDisplayId(id)};
+        if (id.isVirtual() && (id.value & FLAG_GPU)) {
+            return GpuVirtualDisplayId(id);
         }
         return std::nullopt;
     }
 
 private:
+    struct HashTag {}; // Disambiguate with BaseId constructor.
+    constexpr GpuVirtualDisplayId(HashTag, uint64_t hash)
+          : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {}
+
     explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
 };
 
@@ -169,7 +181,7 @@
         if (GpuVirtualDisplayId::tryCast(id)) {
             return std::nullopt;
         }
-        return {HalDisplayId(id)};
+        return HalDisplayId(id);
     }
 
 private:
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 8ddee7e..ef686df 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -24,7 +24,7 @@
     constexpr uint8_t port = 1;
     constexpr uint16_t manufacturerId = 13;
     constexpr uint32_t modelHash = 42;
-    PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+    const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
     EXPECT_EQ(port, id.getPort());
     EXPECT_EQ(manufacturerId, id.getManufacturerId());
     EXPECT_FALSE(VirtualDisplayId::tryCast(id));
@@ -39,7 +39,7 @@
 
 TEST(DisplayIdTest, createPhysicalIdFromPort) {
     constexpr uint8_t port = 3;
-    PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
+    const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
     EXPECT_EQ(port, id.getPort());
     EXPECT_FALSE(VirtualDisplayId::tryCast(id));
     EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
@@ -52,7 +52,34 @@
 }
 
 TEST(DisplayIdTest, createGpuVirtualId) {
-    GpuVirtualDisplayId id(42);
+    const GpuVirtualDisplayId id(42);
+    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+    EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
+}
+
+TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) {
+    const VirtualDisplayId id(GpuVirtualDisplayId(42));
+    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+    EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
+    EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
+}
+
+TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) {
+    static const std::string kUniqueId("virtual:ui:DisplayId_test");
+    const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId);
+    ASSERT_TRUE(idOpt.has_value());
+    const GpuVirtualDisplayId id = idOpt.value();
     EXPECT_TRUE(VirtualDisplayId::tryCast(id));
     EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
@@ -64,7 +91,7 @@
 }
 
 TEST(DisplayIdTest, createHalVirtualId) {
-    HalVirtualDisplayId id(42);
+    const HalVirtualDisplayId id(42);
     EXPECT_TRUE(VirtualDisplayId::tryCast(id));
     EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
@@ -75,4 +102,16 @@
     EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
 }
 
+TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) {
+    const VirtualDisplayId id(HalVirtualDisplayId(42));
+    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+    EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
+    EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value());
+}
+
 } // namespace android::ui
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 1ada33e..5b5afd3 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -254,6 +254,7 @@
         }
     }
 
+    std::vector<std::string> extensionStrings;
     { // scope for lock
         std::lock_guard<std::mutex> _l(lock);
 
@@ -315,16 +316,14 @@
         }
         mClientApiString = sClientApiString;
 
-        mExtensionString = gBuiltinExtensionString;
-
         // b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
         // device's present timestamps are reliable (which may not be the case on emulators).
         if (cnx->angleLoaded) {
             if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
-                mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
+                extensionStrings.push_back("EGL_ANDROID_get_frame_timestamps");
             }
         } else {
-            mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
+            extensionStrings.push_back("EGL_ANDROID_get_frame_timestamps");
         }
 
         hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
@@ -335,10 +334,12 @@
 
         // Add wide-color extensions if device can support wide-color
         if (wideColorBoardConfig && hasColorSpaceSupport) {
-            mExtensionString.append(
-                    "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear "
-                    "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "
-                    "EGL_EXT_gl_colorspace_display_p3_passthrough ");
+            std::vector<std::string> wideColorExtensions =
+                    {"EGL_EXT_gl_colorspace_scrgb", "EGL_EXT_gl_colorspace_scrgb_linear",
+                     "EGL_EXT_gl_colorspace_display_p3_linear", "EGL_EXT_gl_colorspace_display_p3",
+                     "EGL_EXT_gl_colorspace_display_p3_passthrough"};
+            extensionStrings.insert(extensionStrings.end(), wideColorExtensions.begin(),
+                                    wideColorExtensions.end());
         }
 
         bool hasHdrBoardConfig = android::sysprop::has_HDR_display(false);
@@ -348,9 +349,11 @@
             // Typically that means there is an HDR capable display attached, but could be
             // support for attaching an HDR display. In either case, advertise support for
             // HDR color spaces.
-            mExtensionString.append("EGL_EXT_gl_colorspace_bt2020_hlg "
-                                    "EGL_EXT_gl_colorspace_bt2020_linear "
-                                    "EGL_EXT_gl_colorspace_bt2020_pq ");
+            std::vector<std::string> hdrExtensions = {"EGL_EXT_gl_colorspace_bt2020_hlg",
+                                                      "EGL_EXT_gl_colorspace_bt2020_linear",
+                                                      "EGL_EXT_gl_colorspace_bt2020_pq"};
+            extensionStrings.insert(extensionStrings.end(), hdrExtensions.begin(),
+                                    hdrExtensions.end());
         }
 
         char const* start = gExtensionString;
@@ -361,7 +364,7 @@
                 // NOTE: we could avoid the copy if we had strnstr.
                 const std::string ext(start, len);
                 if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
-                    mExtensionString.append(ext + " ");
+                    extensionStrings.push_back(ext);
                 }
                 // advance to the next extension name, skipping the space.
                 start += len;
@@ -388,6 +391,14 @@
         refCond.notify_all();
     }
 
+    auto mergeExtensionStrings = [](const std::vector<std::string>& strings) {
+        std::ostringstream combinedStringStream;
+        std::copy(strings.begin(), strings.end(),
+                  std::ostream_iterator<std::string>(combinedStringStream, " "));
+        // gBuiltinExtensionString already has a trailing space so is added here
+        return gBuiltinExtensionString + combinedStringStream.str();
+    };
+    mExtensionString = mergeExtensionStrings(extensionStrings);
     return EGL_TRUE;
 }
 
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index d96a895..aebd3f2 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -37,6 +37,11 @@
         "libSurfaceFlingerProp",
     ],
 
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+
     include_dirs: [
         "frameworks/native/opengl/libs",
         "frameworks/native/opengl/libs/EGL",
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index cbe4ef9..503d7df 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <gtest/gtest.h>
+#include <gmock/gmock.h>
 
 #include <SurfaceFlingerProperties.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
@@ -29,6 +30,8 @@
 #include <gui/IGraphicBufferConsumer.h>
 #include <gui/BufferQueue.h>
 
+#include "egl_display.h"
+
 bool hasEglExtension(EGLDisplay dpy, const char* extensionName) {
     const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
     size_t cropExtLen = strlen(extensionName);
@@ -1011,4 +1014,57 @@
 
     EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
 }
+
+TEST_F(EGLTest, EGLCheckExtensionString) {
+    // check that the format of the extension string is correct
+
+    egl_display_t* display = egl_display_t::get(mEglDisplay);
+    ASSERT_NE(display, nullptr);
+
+    std::string extensionStrRegex = "((EGL_ANDROID_front_buffer_auto_refresh|"
+       "EGL_ANDROID_get_native_client_buffer|"
+       "EGL_ANDROID_presentation_time|"
+       "EGL_EXT_surface_CTA861_3_metadata|"
+       "EGL_EXT_surface_SMPTE2086_metadata|"
+       "EGL_KHR_get_all_proc_addresses|"
+       "EGL_KHR_swap_buffers_with_damage|"
+       "EGL_ANDROID_get_frame_timestamps|"
+       "EGL_EXT_gl_colorspace_scrgb|"
+       "EGL_EXT_gl_colorspace_scrgb_linear|"
+       "EGL_EXT_gl_colorspace_display_p3_linear|"
+       "EGL_EXT_gl_colorspace_display_p3|"
+       "EGL_EXT_gl_colorspace_display_p3_passthrough|"
+       "EGL_EXT_gl_colorspace_bt2020_hlg|"
+       "EGL_EXT_gl_colorspace_bt2020_linear|"
+       "EGL_EXT_gl_colorspace_bt2020_pq|"
+       "EGL_ANDROID_image_native_buffer|"
+       "EGL_ANDROID_native_fence_sync|"
+       "EGL_ANDROID_recordable|"
+       "EGL_EXT_create_context_robustness|"
+       "EGL_EXT_image_gl_colorspace|"
+       "EGL_EXT_pixel_format_float|"
+       "EGL_EXT_protected_content|"
+       "EGL_EXT_yuv_surface|"
+       "EGL_IMG_context_priority|"
+       "EGL_KHR_config_attribs|"
+       "EGL_KHR_create_context|"
+       "EGL_KHR_fence_sync|"
+       "EGL_KHR_gl_colorspace|"
+       "EGL_KHR_gl_renderbuffer_image|"
+       "EGL_KHR_gl_texture_2D_image|"
+       "EGL_KHR_gl_texture_3D_image|"
+       "EGL_KHR_gl_texture_cubemap_image|"
+       "EGL_KHR_image|"
+       "EGL_KHR_image_base|"
+       "EGL_KHR_mutable_render_buffer|"
+       "EGL_KHR_no_config_context|"
+       "EGL_KHR_partial_update|"
+       "EGL_KHR_surfaceless_context|"
+       "EGL_KHR_wait_sync|"
+       "EGL_EXT_buffer_age|"
+       "EGL_KHR_reusable_sync|"
+       "EGL_NV_context_priority_realtime) )+";
+    EXPECT_THAT(display->getExtensionString(), testing::MatchesRegex(extensionStrRegex));
+}
+
 }
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 8a1eed6..7d3a2df 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -30,10 +30,6 @@
 
 namespace android {
 
-namespace input_flags = com::android::input::flags;
-static const bool HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS =
-        input_flags::hide_pointer_indicators_for_secure_windows();
-
 namespace {
 
 bool isFromMouse(const NotifyMotionArgs& args) {
@@ -89,12 +85,48 @@
     }
 }
 
+// filters and returns a set of privacy sensitive displays that are currently visible.
+std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
+        const std::vector<gui::WindowInfo>& windowInfos) {
+    std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
+    for (const auto& windowInfo : windowInfos) {
+        if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
+            windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
+            privacySensitiveDisplays.insert(windowInfo.displayId);
+        }
+    }
+    return privacySensitiveDisplays;
+}
+
 } // namespace
 
 // --- PointerChoreographer ---
 
-PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
+PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
                                            PointerChoreographerPolicyInterface& policy)
+      : PointerChoreographer(
+                inputListener, policy,
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+                    auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
+                                                      std::vector<android::gui::DisplayInfo>{});
+#if defined(__ANDROID__)
+                    SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
+                                                                                &initialInfo);
+#endif
+                    return initialInfo.first;
+                },
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+#if defined(__ANDROID__)
+                    SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+#endif
+                }) {
+}
+
+PointerChoreographer::PointerChoreographer(
+        android::InputListenerInterface& listener,
+        android::PointerChoreographerPolicyInterface& policy,
+        const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
+        const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
       : mTouchControllerConstructor([this]() {
             return mPolicy.createPointerController(
                     PointerControllerInterface::ControllerType::TOUCH);
@@ -104,7 +136,9 @@
         mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
         mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
         mShowTouchesEnabled(false),
-        mStylusPointerIconEnabled(false) {}
+        mStylusPointerIconEnabled(false),
+        mRegisterListener(registerListener),
+        mUnregisterListener(unregisterListener) {}
 
 PointerChoreographer::~PointerChoreographer() {
     std::scoped_lock _l(mLock);
@@ -112,6 +146,7 @@
         return;
     }
     mWindowInfoListener->onPointerChoreographerDestroyed();
+    mUnregisterListener(mWindowInfoListener);
 }
 
 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
@@ -246,10 +281,13 @@
     }
 
     // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
-    auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
-                                                              getMouseControllerConstructor(
-                                                                      args.displayId));
-    // TODO (b/325252005): Add handing for drawing tablets mouse pointer controller
+    auto [it, controllerAdded] =
+            mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
+                                                       getMouseControllerConstructor(
+                                                               args.displayId));
+    if (controllerAdded) {
+        onControllerAddedOrRemovedLocked();
+    }
 
     PointerControllerInterface& pc = *it->second;
 
@@ -290,7 +328,7 @@
     auto [it, controllerAdded] =
             mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
     if (controllerAdded) {
-        onControllerAddedOrRemoved();
+        onControllerAddedOrRemovedLocked();
     }
 
     PointerControllerInterface& pc = *it->second;
@@ -326,10 +364,12 @@
     }
 
     // Get the stylus pointer controller for the device, or create one if it doesn't exist.
-    auto [it, _] =
+    auto [it, controllerAdded] =
             mStylusPointersByDevice.try_emplace(args.deviceId,
                                                 getStylusControllerConstructor(args.displayId));
-    // TODO (b/325252005): Add handing for stylus pointer controller
+    if (controllerAdded) {
+        onControllerAddedOrRemovedLocked();
+    }
 
     PointerControllerInterface& pc = *it->second;
 
@@ -369,30 +409,57 @@
     mTouchPointersByDevice.erase(args.deviceId);
     mStylusPointersByDevice.erase(args.deviceId);
     mDrawingTabletPointersByDevice.erase(args.deviceId);
-    onControllerAddedOrRemoved();
+    onControllerAddedOrRemovedLocked();
 }
 
-void PointerChoreographer::onControllerAddedOrRemoved() {
-    if (!HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS) {
+void PointerChoreographer::onControllerAddedOrRemovedLocked() {
+    if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
         return;
     }
-    bool requireListener = !mTouchPointersByDevice.empty();
-    // TODO (b/325252005): Update for other types of pointer controllers
+    bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
+            !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
 
     if (requireListener && mWindowInfoListener == nullptr) {
         mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
-        auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
-                                          std::vector<android::gui::DisplayInfo>{});
-#if defined(__ANDROID__)
-        SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener,
-                                                                    &initialInfo);
-#endif
-        onWindowInfosChangedLocked(initialInfo.first);
+        mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
+        onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
     } else if (!requireListener && mWindowInfoListener != nullptr) {
-#if defined(__ANDROID__)
-        SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener);
-#endif
+        mUnregisterListener(mWindowInfoListener);
         mWindowInfoListener = nullptr;
+    } else if (requireListener && mWindowInfoListener != nullptr) {
+        // controller may have been added to an existing privacy sensitive display, we need to
+        // update all controllers again
+        onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
+    }
+}
+
+void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
+        const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
+    for (auto& [_, pc] : mTouchPointersByDevice) {
+        pc->clearSkipScreenshotFlags();
+        for (auto displayId : privacySensitiveDisplays) {
+            pc->setSkipScreenshotFlagForDisplay(displayId);
+        }
+    }
+
+    for (auto& [displayId, pc] : mMousePointersByDisplay) {
+        if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
+            pc->setSkipScreenshotFlagForDisplay(displayId);
+        } else {
+            pc->clearSkipScreenshotFlags();
+        }
+    }
+
+    for (auto* pointerControllerByDevice :
+         {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
+        for (auto& [_, pc] : *pointerControllerByDevice) {
+            auto displayId = pc->getDisplayId();
+            if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
+                pc->setSkipScreenshotFlagForDisplay(displayId);
+            } else {
+                pc->clearSkipScreenshotFlags();
+            }
+        }
     }
 }
 
@@ -407,10 +474,10 @@
     mNextListener.notify(args);
 }
 
-void PointerChoreographer::onWindowInfosChanged(
-        const std::vector<android::gui::WindowInfo>& windowInfos) {
+void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
+        const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
     std::scoped_lock _l(mLock);
-    onWindowInfosChangedLocked(windowInfos);
+    onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
 }
 
 void PointerChoreographer::dump(std::string& dump) {
@@ -467,7 +534,7 @@
     if (it == mMousePointersByDisplay.end()) {
         it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
                      .first;
-        // TODO (b/325252005): Add handing for mouse pointer controller
+        onControllerAddedOrRemovedLocked();
     }
 
     return {displayId, *it->second};
@@ -509,7 +576,9 @@
             auto [mousePointerIt, isNewMousePointer] =
                     mMousePointersByDisplay.try_emplace(displayId,
                                                         getMouseControllerConstructor(displayId));
-            // TODO (b/325252005): Add handing for mouse pointer controller
+            if (isNewMousePointer) {
+                onControllerAddedOrRemovedLocked();
+            }
 
             mMouseDevices.emplace(info.getId());
             if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
@@ -549,7 +618,7 @@
                 mInputDeviceInfos.end();
     });
 
-    onControllerAddedOrRemoved();
+    onControllerAddedOrRemovedLocked();
 
     // Check if we need to notify the policy if there's a change on the pointer display ID.
     return calculatePointerDisplayChangeToNotify();
@@ -715,31 +784,6 @@
     return false;
 }
 
-void PointerChoreographer::onWindowInfosChangedLocked(
-        const std::vector<android::gui::WindowInfo>& windowInfos) {
-    // Mark all spot controllers secure on displays containing secure windows and
-    // remove secure flag from others if required
-    std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
-    std::unordered_set<ui::LogicalDisplayId> allDisplayIds;
-    for (const auto& windowInfo : windowInfos) {
-        allDisplayIds.insert(windowInfo.displayId);
-        if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
-            windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
-            privacySensitiveDisplays.insert(windowInfo.displayId);
-        }
-    }
-
-    for (auto& it : mTouchPointersByDevice) {
-        auto& pc = it.second;
-        for (ui::LogicalDisplayId displayId : allDisplayIds) {
-            pc->setSkipScreenshot(displayId,
-                                  privacySensitiveDisplays.find(displayId) !=
-                                          privacySensitiveDisplays.end());
-        }
-    }
-    // TODO (b/325252005): update skip screenshot flag for other types of pointer controllers
-}
-
 void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
     std::scoped_lock lock(mLock);
     if (visible) {
@@ -793,9 +837,27 @@
 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
         const gui::WindowInfosUpdate& windowInfosUpdate) {
     std::scoped_lock _l(mListenerLock);
-    if (mPointerChoreographer != nullptr) {
-        mPointerChoreographer->onWindowInfosChanged(windowInfosUpdate.windowInfos);
+    if (mPointerChoreographer == nullptr) {
+        return;
     }
+    auto newPrivacySensitiveDisplays =
+            getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
+    if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
+        mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
+        mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
+    }
+}
+
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
+        const std::vector<gui::WindowInfo>& windowInfos) {
+    std::scoped_lock _l(mListenerLock);
+    mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
+}
+
+std::unordered_set<ui::LogicalDisplayId /*displayId*/>
+PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
+    std::scoped_lock _l(mListenerLock);
+    return mPrivacySensitiveDisplays;
 }
 
 void PointerChoreographer::PointerChoreographerDisplayInfoListener::
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 12316c0..d9b075f 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -23,6 +23,7 @@
 #include <android-base/thread_annotations.h>
 #include <gui/WindowInfosListener.h>
 #include <type_traits>
+#include <unordered_set>
 
 namespace android {
 
@@ -107,9 +108,6 @@
     void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
 
-    // Public because it's also used by tests to simulate the WindowInfosListener callback
-    void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos);
-
     void dump(std::string& dump) override;
 
 private:
@@ -133,20 +131,34 @@
     void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
     void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
     void processDeviceReset(const NotifyDeviceResetArgs& args);
-    void onControllerAddedOrRemoved() REQUIRES(mLock);
-    void onWindowInfosChangedLocked(const std::vector<android::gui::WindowInfo>& windowInfos)
+    void onControllerAddedOrRemovedLocked() REQUIRES(mLock);
+    void onPrivacySensitiveDisplaysChangedLocked(
+            const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
             REQUIRES(mLock);
+    void onPrivacySensitiveDisplaysChanged(
+            const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays);
 
+    /* This listener keeps tracks of visible privacy sensitive displays and updates the
+     * choreographer if there are any changes.
+     *
+     * Listener uses mListenerLock to guard all private data as choreographer and SurfaceComposer
+     * both can call into the listener. To prevent deadlocks Choreographer can call listener with
+     * its lock held, but listener must not call choreographer with its lock.
+     */
     class PointerChoreographerDisplayInfoListener : public gui::WindowInfosListener {
     public:
         explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
               : mPointerChoreographer(pc){};
         void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
+        void setInitialDisplayInfos(const std::vector<gui::WindowInfo>& windowInfos);
+        std::unordered_set<ui::LogicalDisplayId /*displayId*/> getPrivacySensitiveDisplays();
         void onPointerChoreographerDestroyed();
 
     private:
         std::mutex mListenerLock;
         PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+        std::unordered_set<ui::LogicalDisplayId /*displayId*/> mPrivacySensitiveDisplays
+                GUARDED_BY(mListenerLock);
     };
     sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener GUARDED_BY(mLock);
 
@@ -180,6 +192,20 @@
     bool mShowTouchesEnabled GUARDED_BY(mLock);
     bool mStylusPointerIconEnabled GUARDED_BY(mLock);
     std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
+
+protected:
+    using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>(
+            const sp<android::gui::WindowInfosListener>&)>;
+    using WindowListenerUnregisterConsumer =
+            std::function<void(const sp<android::gui::WindowInfosListener>&)>;
+    explicit PointerChoreographer(InputListenerInterface& listener,
+                                  PointerChoreographerPolicyInterface&,
+                                  const WindowListenerRegisterConsumer& registerListener,
+                                  const WindowListenerUnregisterConsumer& unregisterListener);
+
+private:
+    const WindowListenerRegisterConsumer mRegisterListener;
+    const WindowListenerUnregisterConsumer mUnregisterListener;
 };
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 29aa3c3..1a0ec48 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -56,7 +56,9 @@
 
 cc_defaults {
     name: "libinputdispatcher_defaults",
-    srcs: [":libinputdispatcher_sources"],
+    srcs: [
+        ":libinputdispatcher_sources",
+    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -78,6 +80,7 @@
         "libattestation",
         "libgui_window_info_static",
         "libperfetto_client_experimental",
+        "perfetto_winscope_extensions_zero",
     ],
     target: {
         android: {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index dae2b61..5ed5eb8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -444,10 +444,12 @@
             newCoords.copyFrom(motionEntry.pointerCoords[i]);
             // First, apply the current pointer's transform to update the coordinates into
             // window space.
-            newCoords.transform(currTransform);
+            MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source,
+                                                           motionEntry.flags, currTransform);
             // Next, apply the inverse transform of the normalized coordinates so the
             // current coordinates are transformed into the normalized coordinate space.
-            newCoords.transform(inverseTransform);
+            MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source,
+                                                           motionEntry.flags, inverseTransform);
         }
     }
 
@@ -1894,8 +1896,6 @@
                 doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
             };
             postCommandLocked(std::move(command));
-            // Poke user activity for keys not passed to user
-            pokeUserActivityLocked(*entry);
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
@@ -1912,8 +1912,12 @@
                            *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                              : InputEventInjectionResult::FAILED);
         mReporter->reportDroppedKey(entry->id);
-        // Poke user activity for undispatched keys
-        pokeUserActivityLocked(*entry);
+        // Poke user activity for consumed keys, as it may have not been reported due to
+        // the focused window requesting user activity to be disabled
+        if (*dropReason == DropReason::POLICY &&
+            mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
+            pokeUserActivityLocked(*entry);
+        }
         return true;
     }
 
@@ -3313,22 +3317,16 @@
             if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
                 return;
             }
-            // If the key code is unknown, we don't consider it user activity
-            if (keyEntry.keyCode == AKEYCODE_UNKNOWN) {
-                return;
-            }
             // Don't inhibit events that were intercepted or are not passed to
             // the apps, like system shortcuts
             if (windowDisablingUserActivityInfo != nullptr &&
-                keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP &&
-                keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) {
+                keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) {
                 if (DEBUG_DISPATCH_CYCLE) {
                     ALOGD("Not poking user activity: disabled by window '%s'.",
                           windowDisablingUserActivityInfo->name.c_str());
                 }
                 return;
             }
-
             break;
         }
         default: {
@@ -4885,10 +4883,11 @@
 
             mLock.lock();
 
-            if (policyFlags & POLICY_FLAG_FILTERED) {
-                // The events from InputFilter impersonate real hardware devices. Check these
-                // events for consistency and print an error. An inconsistent event sent from
-                // InputFilter could cause a crash in the later stages of dispatching pipeline.
+            {
+                // Verify all injected streams, whether the injection is coming from apps or from
+                // input filter. Print an error if the stream becomes inconsistent with this event.
+                // An inconsistent injected event sent could cause a crash in the later stages of
+                // dispatching pipeline.
                 auto [it, _] =
                         mInputFilterVerifiersByDisplay.try_emplace(displayId,
                                                                    std::string("Injection on ") +
@@ -5132,8 +5131,8 @@
     }
     for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
         entry.pointerCoords[i] =
-                MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
-                                                        entry.pointerCoords[i]);
+                MotionEvent::calculateTransformedCoords(entry.source, entry.flags,
+                                                        transformToDisplay, entry.pointerCoords[i]);
     }
 }
 
@@ -5528,6 +5527,10 @@
             }
             mFocusedDisplayId = displayId;
 
+            // Only a window on the focused display can have Pointer Capture, so disable the active
+            // Pointer Capture session if there is one, since the focused display changed.
+            disablePointerCaptureForcedLocked();
+
             // Find new focused window and validate
             sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
             sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
@@ -6388,9 +6391,8 @@
         }
 
         if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
-            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry));
             fallbackKeyEntry =
-                    afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+                    afterKeyEventLockedInterruptable(connection, &dispatchEntry, handled);
         }
     } // End critical section: The -LockedInterruptable methods may have released the lock.
 
@@ -6614,8 +6616,17 @@
 }
 
 std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
-        const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
-        const KeyEntry& keyEntry, bool handled) {
+        const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) {
+    // The dispatchEntry is currently valid, but it might point to a deleted object after we release
+    // the lock. For simplicity, make copies of the data of interest here and assume that
+    // 'dispatchEntry' is not valid after this section.
+    // Hold a strong reference to the EventEntry to ensure it's valid for the duration of this
+    // function, even if the DispatchEntry gets destroyed and releases its share of the ownership.
+    std::shared_ptr<const EventEntry> eventEntry = dispatchEntry->eventEntry;
+    const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget();
+    const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(eventEntry));
+    // To prevent misuse, ensure dispatchEntry is no longer valid.
+    dispatchEntry = nullptr;
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
@@ -6632,7 +6643,7 @@
         connection->inputState.removeFallbackKey(originalKeyCode);
     }
 
-    if (handled || !dispatchEntry.hasForegroundTarget()) {
+    if (handled || !hasForegroundTarget) {
         // If the application handles the original key for which we previously
         // generated a fallback or if the window is not a foreground window,
         // then cancel the associated fallback key, if any.
@@ -6920,17 +6931,17 @@
         enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
     }
 
-    // If a window has pointer capture, then it must have focus. We need to ensure that this
-    // contract is upheld when pointer capture is being disabled due to a loss of window focus.
-    // If the window loses focus before it loses pointer capture, then the window can be in a state
-    // where it has pointer capture but not focus, violating the contract. Therefore we must
-    // dispatch the pointer capture event before the focus event. Since focus events are added to
-    // the front of the queue (above), we add the pointer capture event to the front of the queue
-    // after the focus events are added. This ensures the pointer capture event ends up at the
-    // front.
-    disablePointerCaptureForcedLocked();
-
     if (mFocusedDisplayId == changes.displayId) {
+        // If a window has pointer capture, then it must have focus and must be on the top-focused
+        // display. We need to ensure that this contract is upheld when pointer capture is being
+        // disabled due to a loss of window focus. If the window loses focus before it loses pointer
+        // capture, then the window can be in a state where it has pointer capture but not focus,
+        // violating the contract. Therefore we must dispatch the pointer capture event before the
+        // focus event. Since focus events are added to the front of the queue (above), we add the
+        // pointer capture event to the front of the queue after the focus events are added. This
+        // ensures the pointer capture event ends up at the front.
+        disablePointerCaptureForcedLocked();
+
         sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
     }
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 6240e7f..e2fc7a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -687,8 +687,8 @@
     std::map<ui::LogicalDisplayId /*displayId*/, InputVerifier> mVerifiersByDisplay;
     // Returns a fallback KeyEntry that should be sent to the connection, if required.
     std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
-            const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
-            const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
+            const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+            bool handled) REQUIRES(mLock);
 
     // Find touched state and touched window by token.
     std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index 2d7554c..0b17507 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -123,7 +123,8 @@
 
             const auto& coords = motion->pointerCoords[i];
             const auto coordsInWindow =
-                    MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords);
+                    MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
+                                                            args.transform, coords);
             auto bits = BitSet64(coords.bits);
             for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
                 const uint32_t axis = bits.clearFirstMarkedBit();
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 9b9633a..3d30ad6 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -23,6 +23,8 @@
 #include <android-base/logging.h>
 #include <binder/IServiceManager.h>
 #include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String16.h>
 
@@ -229,7 +231,9 @@
         }
         const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
         auto tracePacket = ctx.NewTracePacket();
-        auto* inputEvent = tracePacket->set_android_input_event();
+        auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
+                tracePacket->set_winscope_extensions());
+        auto* inputEvent = winscopeExtensions->set_android_input_event();
         auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted()
                                           : inputEvent->set_dispatcher_motion_event();
         AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted);
@@ -253,7 +257,9 @@
         }
         const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
         auto tracePacket = ctx.NewTracePacket();
-        auto* inputEvent = tracePacket->set_android_input_event();
+        auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
+                tracePacket->set_winscope_extensions());
+        auto* inputEvent = winscopeExtensions->set_android_input_event();
         auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted()
                                        : inputEvent->set_dispatcher_key_event();
         AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted);
@@ -277,7 +283,9 @@
         }
         const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
         auto tracePacket = ctx.NewTracePacket();
-        auto* inputEvent = tracePacket->set_android_input_event();
+        auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
+                tracePacket->set_winscope_extensions());
+        auto* inputEvent = winscopeExtensions->set_android_input_event();
         auto* dispatchEvent = isRedacted
                 ? inputEvent->set_dispatcher_window_dispatch_event_redacted()
                 : inputEvent->set_dispatcher_window_dispatch_event();
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index cee44fc..e34ed0f 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -142,10 +142,13 @@
     /* Sets the custom pointer icon for mice or styluses. */
     virtual void setCustomPointerIcon(const SpriteIcon& icon) = 0;
 
-    /* Sets the flag to skip screenshot of the pointer indicators on the display matching the
-     * provided displayId.
+    /* Sets the flag to skip screenshot of the pointer indicators on the display for the specified
+     * displayId. This flag can only be reset with resetSkipScreenshotFlags()
      */
-    virtual void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) = 0;
+    virtual void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) = 0;
+
+    /* Resets the flag to skip screenshot of the pointer indicators for all displays. */
+    virtual void clearSkipScreenshotFlags() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index b807b27..2daf195 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -237,6 +237,12 @@
     mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
     mHasMic = mClasses.test(InputDeviceClass::MIC);
 
+    // Update keyboard type
+    if (mClasses.test(InputDeviceClass::KEYBOARD)) {
+        mContext->getKeyboardClassifier().notifyKeyboardChanged(mId, mIdentifier, mClasses.get());
+        mKeyboardType = mContext->getKeyboardClassifier().getKeyboardType(mId);
+    }
+
     using Change = InputReaderConfiguration::Change;
 
     if (!changes.any() || !isIgnored()) {
@@ -403,7 +409,7 @@
             mDropUntilNextSync = true;
         } else {
             for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
-                out += mapper.process(rawEvent);
+                out += mapper.process(*rawEvent);
             });
         }
         --count;
@@ -445,6 +451,7 @@
                              mHasMic,
                              getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID),
                              {mShouldSmoothScroll}, isEnabled());
+    outDeviceInfo.setKeyboardType(static_cast<int32_t>(mKeyboardType));
 
     for_each_mapper(
             [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
@@ -517,13 +524,9 @@
 
     // Keyboard-like devices.
     uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
     if (classes.test(InputDeviceClass::KEYBOARD)) {
         keyboardSource |= AINPUT_SOURCE_KEYBOARD;
     }
-    if (classes.test(InputDeviceClass::ALPHAKEY)) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
     if (classes.test(InputDeviceClass::DPAD)) {
         keyboardSource |= AINPUT_SOURCE_DPAD;
     }
@@ -532,8 +535,8 @@
     }
 
     if (keyboardSource != 0) {
-        mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig,
-                                                                 keyboardSource, keyboardType));
+        mappers.push_back(
+                createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig, keyboardSource));
     }
 
     // Cursor-like devices.
@@ -730,6 +733,13 @@
     return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt;
 }
 
+void InputDevice::setKeyboardType(KeyboardType keyboardType) {
+    if (mKeyboardType != keyboardType) {
+        mKeyboardType = keyboardType;
+        bumpGeneration();
+    }
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index b9523ef..ab13ad4 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -102,6 +102,7 @@
         mEventHub(eventHub),
         mPolicy(policy),
         mNextListener(listener),
+        mKeyboardClassifier(std::make_unique<KeyboardClassifier>()),
         mGlobalMetaState(AMETA_NONE),
         mLedMetaState(AMETA_NONE),
         mGeneration(1),
@@ -1076,4 +1077,8 @@
     return mIdGenerator.nextId();
 }
 
+KeyboardClassifier& InputReader::ContextImpl::getKeyboardClassifier() {
+    return *mReader->mKeyboardClassifier;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 27b9d23..49ad8b5 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -418,7 +418,11 @@
         }
         rawInfos.insert_or_assign(rawId, rawInfo.value());
         // Check if this is a group LEDs for player ID
-        std::regex lightPattern("([a-z]+)([0-9]+)");
+        // The name for the light has already been parsed and is the `function`
+        // value; for player ID lights the function is expected to be `player-#`.
+        // However, the Sony driver will use `sony#` instead on SIXAXIS
+        // gamepads.
+        std::regex lightPattern("(player|sony)-?([0-9]+)");
         std::smatch results;
         if (std::regex_match(rawInfo->name, results, lightPattern)) {
             std::string commonName = results[1].str();
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 39d2f5b..7cf584d 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -85,64 +85,67 @@
 
 /*
  * Input device classes.
+ *
+ * These classes are duplicated in rust side here: /frameworks/native/libs/input/rust/input.rs.
+ * If any new classes are added, we need to add them in rust input side too.
  */
 enum class InputDeviceClass : uint32_t {
     /* The input device is a keyboard or has buttons. */
-    KEYBOARD = 0x00000001,
+    KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD,
 
     /* The input device is an alpha-numeric keyboard (not just a dial pad). */
-    ALPHAKEY = 0x00000002,
+    ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY,
 
     /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
-    TOUCH = 0x00000004,
+    TOUCH = android::os::IInputConstants::DEVICE_CLASS_TOUCH,
 
     /* The input device is a cursor device such as a trackball or mouse. */
-    CURSOR = 0x00000008,
+    CURSOR = android::os::IInputConstants::DEVICE_CLASS_CURSOR,
 
     /* The input device is a multi-touch touchscreen or touchpad. */
-    TOUCH_MT = 0x00000010,
+    TOUCH_MT = android::os::IInputConstants::DEVICE_CLASS_TOUCH_MT,
 
     /* The input device is a directional pad (implies keyboard, has DPAD keys). */
-    DPAD = 0x00000020,
+    DPAD = android::os::IInputConstants::DEVICE_CLASS_DPAD,
 
     /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
-    GAMEPAD = 0x00000040,
+    GAMEPAD = android::os::IInputConstants::DEVICE_CLASS_GAMEPAD,
 
     /* The input device has switches. */
-    SWITCH = 0x00000080,
+    SWITCH = android::os::IInputConstants::DEVICE_CLASS_SWITCH,
 
     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */
-    JOYSTICK = 0x00000100,
+    JOYSTICK = android::os::IInputConstants::DEVICE_CLASS_JOYSTICK,
 
     /* The input device has a vibrator (supports FF_RUMBLE). */
-    VIBRATOR = 0x00000200,
+    VIBRATOR = android::os::IInputConstants::DEVICE_CLASS_VIBRATOR,
 
     /* The input device has a microphone. */
-    MIC = 0x00000400,
+    MIC = android::os::IInputConstants::DEVICE_CLASS_MIC,
 
     /* The input device is an external stylus (has data we want to fuse with touch data). */
-    EXTERNAL_STYLUS = 0x00000800,
+    EXTERNAL_STYLUS = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS,
 
     /* The input device has a rotary encoder */
-    ROTARY_ENCODER = 0x00001000,
+    ROTARY_ENCODER = android::os::IInputConstants::DEVICE_CLASS_ROTARY_ENCODER,
 
     /* The input device has a sensor like accelerometer, gyro, etc */
-    SENSOR = 0x00002000,
+    SENSOR = android::os::IInputConstants::DEVICE_CLASS_SENSOR,
 
     /* The input device has a battery */
-    BATTERY = 0x00004000,
+    BATTERY = android::os::IInputConstants::DEVICE_CLASS_BATTERY,
 
     /* The input device has sysfs controllable lights */
-    LIGHT = 0x00008000,
+    LIGHT = android::os::IInputConstants::DEVICE_CLASS_LIGHT,
 
     /* The input device is a touchpad, requiring an on-screen cursor. */
-    TOUCHPAD = 0x00010000,
+    TOUCHPAD = android::os::IInputConstants::DEVICE_CLASS_TOUCHPAD,
 
     /* The input device is virtual (not a real device, not part of UI configuration). */
-    VIRTUAL = 0x40000000,
+    VIRTUAL = android::os::IInputConstants::DEVICE_CLASS_VIRTUAL,
 
     /* The input device is external (not built-in). */
-    EXTERNAL = 0x80000000,
+    EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL,
 };
 
 enum class SysfsClass : uint32_t {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4c9af2e..2a7e262 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -79,6 +79,8 @@
 
     inline bool isIgnored() { return !getMapperCount() && !mController; }
 
+    inline KeyboardType getKeyboardType() const { return mKeyboardType; }
+
     bool isEnabled();
 
     void dump(std::string& dump, const std::string& eventHubDevStr);
@@ -124,6 +126,8 @@
 
     void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
 
+    void setKeyboardType(KeyboardType keyboardType);
+
     void bumpGeneration();
 
     [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -196,6 +200,7 @@
     uint32_t mSources;
     bool mIsWaking;
     bool mIsExternal;
+    KeyboardType mKeyboardType = KeyboardType::NONE;
     std::optional<uint8_t> mAssociatedDisplayPort;
     std::optional<std::string> mAssociatedDisplayUniqueIdByPort;
     std::optional<std::string> mAssociatedDisplayUniqueIdByDescriptor;
@@ -470,6 +475,10 @@
     }
     inline void bumpGeneration() { mDevice.bumpGeneration(); }
     inline const PropertyMap& getConfiguration() const { return mDevice.getConfiguration(); }
+    inline KeyboardType getKeyboardType() const { return mDevice.getKeyboardType(); }
+    inline void setKeyboardType(KeyboardType keyboardType) {
+        return mDevice.setKeyboardType(keyboardType);
+    }
 
 private:
     InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 7e701c5..6f8c289 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -157,6 +157,7 @@
         void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock)
                 REQUIRES(mLock) override;
         nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+        KeyboardClassifier& getKeyboardClassifier() override;
     } mContext;
 
     friend class ContextImpl;
@@ -176,6 +177,10 @@
 
     // The next stage that should receive the events generated inside InputReader.
     InputListenerInterface& mNextListener;
+
+    // Classifier for keyboard/keyboard-like devices
+    std::unique_ptr<KeyboardClassifier> mKeyboardClassifier;
+
     // As various events are generated inside InputReader, they are stored inside this list. The
     // list can only be accessed with the lock, so the events inside it are well-ordered.
     // Once the reader is done working, these events will be swapped into a temporary storage and
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 907a49f..e0e0ac2 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <input/InputDevice.h>
+#include <input/KeyboardClassifier.h>
 #include "NotifyArgs.h"
 
 #include <vector>
@@ -64,6 +65,8 @@
 
     virtual void setLastKeyDownTimestamp(nsecs_t when) = 0;
     virtual nsecs_t getLastKeyDownTimestamp() = 0;
+
+    virtual KeyboardClassifier& getKeyboardClassifier() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index 09a5f08..90685de 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -155,8 +155,8 @@
         mMotionAccumulator.finishSync();
     }
 
-    mCursorButtonAccumulator.process(&rawEvent);
-    mMotionAccumulator.process(&rawEvent);
+    mCursorButtonAccumulator.process(rawEvent);
+    mMotionAccumulator.process(rawEvent);
     return out;
 }
 
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index c67314d..20cdb59 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -55,14 +55,14 @@
     mRelY = 0;
 }
 
-void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_REL) {
-        switch (rawEvent->code) {
+void CursorMotionAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_REL) {
+        switch (rawEvent.code) {
             case REL_X:
-                mRelX = rawEvent->value;
+                mRelX = rawEvent.value;
                 break;
             case REL_Y:
-                mRelY = rawEvent->value;
+                mRelY = rawEvent.value;
                 break;
         }
     }
@@ -215,16 +215,16 @@
     return InputMapper::reset(when);
 }
 
-std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> CursorInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
     mCursorButtonAccumulator.process(rawEvent);
     mCursorMotionAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
 
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
         const auto [eventTime, readTime] =
                 applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(),
-                                                   rawEvent->when, rawEvent->readTime,
+                                                   rawEvent.when, rawEvent.readTime,
                                                    mLastEventTime);
         out += sync(eventTime, readTime);
         mLastEventTime = eventTime;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 75ca9c0..2108488 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -34,7 +34,7 @@
     CursorMotionAccumulator();
     void reset(InputDeviceContext& deviceContext);
 
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
     void finishSync();
 
     inline int32_t getRelativeX() const { return mRelX; }
@@ -62,7 +62,7 @@
                                                     const InputReaderConfiguration& readerConfig,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 987d2d0..3af1d04 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -61,13 +61,13 @@
     return InputMapper::reset(when);
 }
 
-std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
     mSingleTouchMotionAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        out += sync(rawEvent->when);
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        out += sync(rawEvent.when);
     }
     return out;
 }
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 97df02b..c040a7b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -39,7 +39,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index c7eea0e..2c51448 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -78,7 +78,7 @@
                                                             const InputReaderConfiguration& config,
                                                             ConfigurationChanges changes);
     [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
-    [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
+    [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent& rawEvent) = 0;
     [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
 
     virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 5ce4d30..41e018d 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -259,29 +259,29 @@
     return InputMapper::reset(when);
 }
 
-std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
-    switch (rawEvent->type) {
+    switch (rawEvent.type) {
         case EV_ABS: {
-            auto it = mAxes.find(rawEvent->code);
+            auto it = mAxes.find(rawEvent.code);
             if (it != mAxes.end()) {
                 Axis& axis = it->second;
                 float newValue, highNewValue;
                 switch (axis.axisInfo.mode) {
                     case AxisInfo::MODE_INVERT:
-                        newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale +
+                        newValue = (axis.rawAxisInfo.maxValue - rawEvent.value) * axis.scale +
                                 axis.offset;
                         highNewValue = 0.0f;
                         break;
                     case AxisInfo::MODE_SPLIT:
-                        if (rawEvent->value < axis.axisInfo.splitValue) {
-                            newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale +
+                        if (rawEvent.value < axis.axisInfo.splitValue) {
+                            newValue = (axis.axisInfo.splitValue - rawEvent.value) * axis.scale +
                                     axis.offset;
                             highNewValue = 0.0f;
-                        } else if (rawEvent->value > axis.axisInfo.splitValue) {
+                        } else if (rawEvent.value > axis.axisInfo.splitValue) {
                             newValue = 0.0f;
                             highNewValue =
-                                    (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale +
+                                    (rawEvent.value - axis.axisInfo.splitValue) * axis.highScale +
                                     axis.highOffset;
                         } else {
                             newValue = 0.0f;
@@ -289,7 +289,7 @@
                         }
                         break;
                     default:
-                        newValue = rawEvent->value * axis.scale + axis.offset;
+                        newValue = rawEvent.value * axis.scale + axis.offset;
                         highNewValue = 0.0f;
                         break;
                 }
@@ -300,9 +300,9 @@
         }
 
         case EV_SYN:
-            switch (rawEvent->code) {
+            switch (rawEvent.code) {
                 case SYN_REPORT:
-                    out += sync(rawEvent->when, rawEvent->readTime, /*force=*/false);
+                    out += sync(rawEvent.when, rawEvent.readTime, /*force=*/false);
                     break;
             }
             break;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 313f092..621d38b 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -35,7 +35,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
 private:
     struct Axis {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 2124555..91ec62d 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -21,6 +21,7 @@
 #include "KeyboardInputMapper.h"
 
 #include <ftl/enum.h>
+#include <input/KeyboardClassifier.h>
 #include <ui/Rotation.h>
 
 namespace android {
@@ -96,8 +97,8 @@
 
 KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
                                          const InputReaderConfiguration& readerConfig,
-                                         uint32_t source, int32_t keyboardType)
-      : InputMapper(deviceContext, readerConfig), mSource(source), mKeyboardType(keyboardType) {}
+                                         uint32_t source)
+      : InputMapper(deviceContext, readerConfig), mSource(source) {}
 
 uint32_t KeyboardInputMapper::getSources() const {
     return mSource;
@@ -131,7 +132,6 @@
 void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
 
-    info.setKeyboardType(mKeyboardType);
     info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
 
     std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
@@ -143,7 +143,6 @@
 void KeyboardInputMapper::dump(std::string& dump) {
     dump += INDENT2 "Keyboard Input Mapper:\n";
     dumpParameters(dump);
-    dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
     dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(getOrientation()).c_str());
     dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
     dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
@@ -237,15 +236,15 @@
     return out;
 }
 
-std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
-    mHidUsageAccumulator.process(*rawEvent);
-    switch (rawEvent->type) {
+    mHidUsageAccumulator.process(rawEvent);
+    switch (rawEvent.type) {
         case EV_KEY: {
-            int32_t scanCode = rawEvent->code;
+            int32_t scanCode = rawEvent.code;
 
             if (isSupportedScanCode(scanCode)) {
-                out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
+                out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0,
                                   scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
             }
             break;
@@ -327,13 +326,24 @@
         keyMetaState = mMetaState;
     }
 
+    DeviceId deviceId = getDeviceId();
+
+    // On first down: Process key for keyboard classification (will send reconfiguration if the
+    // keyboard type change)
+    if (down && !keyDownIndex) {
+        KeyboardClassifier& classifier = getDeviceContext().getContext()->getKeyboardClassifier();
+        classifier.processKey(deviceId, scanCode, keyMetaState);
+        getDeviceContext().setKeyboardType(classifier.getKeyboardType(deviceId));
+    }
+
+    KeyboardType keyboardType = getDeviceContext().getKeyboardType();
     // Any key down on an external keyboard should wake the device.
     // We don't do this for internal keyboards to prevent them from waking up in your pocket.
     // For internal keyboards and devices for which the default wake behavior is explicitly
     // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
     // wake key individually.
     if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
-        !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) {
+        !(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
@@ -341,8 +351,8 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                   mSource, getDisplayId(), policyFlags,
+    out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, deviceId, mSource,
+                                   getDisplayId(), policyFlags,
                                    down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
                                    keyCode, scanCode, keyMetaState, downTime));
     return out;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index f2d3f4d..c7df558 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -36,7 +36,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
     int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
@@ -61,7 +61,6 @@
     };
 
     uint32_t mSource{};
-    int32_t mKeyboardType{};
     std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
 
     std::vector<KeyDown> mKeyDowns{}; // keys that are down
@@ -85,8 +84,7 @@
     } mParameters{};
 
     KeyboardInputMapper(InputDeviceContext& deviceContext,
-                        const InputReaderConfiguration& readerConfig, uint32_t source,
-                        int32_t keyboardType);
+                        const InputReaderConfiguration& readerConfig, uint32_t source);
     void configureParameters();
     void dumpParameters(std::string& dump) const;
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index bca9d91..1986fe2 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -40,7 +40,7 @@
     return TouchInputMapper::reset(when);
 }
 
-std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mMultiTouchMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 5c173f3..cca2327 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -31,7 +31,7 @@
     ~MultiTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
     [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 0ddbc06..27ff52f 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -101,12 +101,12 @@
     return InputMapper::reset(when);
 }
 
-std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
     mRotaryEncoderScrollAccumulator.process(rawEvent);
 
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        out += sync(rawEvent->when, rawEvent->readTime);
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        out += sync(rawEvent.when, rawEvent.readTime);
     }
     return out;
 }
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index fe5d152..14c540b 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -39,7 +39,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
 private:
     CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index a131e35..d7f2993 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -251,35 +251,35 @@
     mPrevMscTime = static_cast<uint32_t>(mscTime);
 }
 
-std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SensorInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
-    switch (rawEvent->type) {
+    switch (rawEvent.type) {
         case EV_ABS: {
-            auto it = mAxes.find(rawEvent->code);
+            auto it = mAxes.find(rawEvent.code);
             if (it != mAxes.end()) {
                 Axis& axis = it->second;
-                axis.newValue = rawEvent->value * axis.scale + axis.offset;
+                axis.newValue = rawEvent.value * axis.scale + axis.offset;
             }
             break;
         }
 
         case EV_SYN:
-            switch (rawEvent->code) {
+            switch (rawEvent.code) {
                 case SYN_REPORT:
                     for (std::pair<const int32_t, Axis>& pair : mAxes) {
                         Axis& axis = pair.second;
                         axis.currentValue = axis.newValue;
                     }
-                    out += sync(rawEvent->when, /*force=*/false);
+                    out += sync(rawEvent.when, /*force=*/false);
                     break;
             }
             break;
 
         case EV_MSC:
-            switch (rawEvent->code) {
+            switch (rawEvent.code) {
                 case MSC_TIMESTAMP:
                     // hardware timestamp is nano seconds
-                    processHardWareTimestamp(rawEvent->when, rawEvent->value);
+                    processHardWareTimestamp(rawEvent.when, rawEvent.value);
                     break;
             }
     }
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index a55dcd1..63bc151 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -40,7 +40,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency) override;
     void disableSensor(InputDeviceSensorType sensorType) override;
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index ed0e270..140bb0c 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -30,7 +30,7 @@
     return TouchInputMapper::reset(when);
 }
 
-std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mSingleTouchMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..bc38711 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -31,7 +31,7 @@
     ~SingleTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
 protected:
     void syncTouch(nsecs_t when, RawState* outState) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 05338da..f131fb7 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -30,16 +30,16 @@
     return AINPUT_SOURCE_SWITCH;
 }
 
-std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent& rawEvent) {
     std::list<NotifyArgs> out;
-    switch (rawEvent->type) {
+    switch (rawEvent.type) {
         case EV_SW:
-            processSwitch(rawEvent->code, rawEvent->value);
+            processSwitch(rawEvent.code, rawEvent.value);
             break;
 
         case EV_SYN:
-            if (rawEvent->code == SYN_REPORT) {
-                out += sync(rawEvent->when);
+            if (rawEvent.code == SYN_REPORT) {
+                out += sync(rawEvent.when);
             }
     }
     return out;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 2fb48bb..5d8aa2c 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -29,7 +29,7 @@
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources() const override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
     virtual void dump(std::string& dump) override;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8b4b691..a383490 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -537,18 +537,6 @@
             return getDeviceContext().getAssociatedViewport();
         }
 
-        const std::optional<std::string> associatedDisplayUniqueIdByDescriptor =
-                getDeviceContext().getAssociatedDisplayUniqueIdByDescriptor();
-        if (associatedDisplayUniqueIdByDescriptor) {
-            return getDeviceContext().getAssociatedViewport();
-        }
-
-        const std::optional<std::string> associatedDisplayUniqueIdByPort =
-                getDeviceContext().getAssociatedDisplayUniqueIdByPort();
-        if (associatedDisplayUniqueIdByPort) {
-            return getDeviceContext().getAssociatedViewport();
-        }
-
         if (mDeviceMode == DeviceMode::POINTER) {
             std::optional<DisplayViewport> viewport =
                     mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
@@ -1415,14 +1403,14 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
-std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchInputMapper::process(const RawEvent& rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     std::list<NotifyArgs> out;
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        out += sync(rawEvent->when, rawEvent->readTime);
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        out += sync(rawEvent.when, rawEvent.readTime);
     }
     return out;
 }
@@ -2355,20 +2343,23 @@
         if (mHaveTilt) {
             float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
             float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
-            orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)));
+            orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)),
+                                         /*isDirectional=*/true);
             tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
         } else {
             tilt = 0;
 
             switch (mCalibration.orientationCalibration) {
                 case Calibration::OrientationCalibration::INTERPOLATED:
-                    orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale);
+                    orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale,
+                                                 /*isDirectional=*/true);
                     break;
                 case Calibration::OrientationCalibration::VECTOR: {
                     int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
                     int32_t c2 = signExtendNybble(in.orientation & 0x0f);
                     if (c1 != 0 || c2 != 0) {
-                        orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f);
+                        orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f,
+                                                     /*isDirectional=*/true);
                         float confidence = hypotf(c1, c2);
                         float scale = 1.0f + confidence / 16.0f;
                         touchMajor *= scale;
@@ -3684,6 +3675,14 @@
     if (mCurrentStreamModifiedByExternalStylus) {
         source |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
     }
+    if (mOrientedRanges.orientation.has_value()) {
+        flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION;
+        if (mOrientedRanges.tilt.has_value()) {
+            // In the current implementation, only devices that report a value for tilt supports
+            // directional orientation.
+            flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
+        }
+    }
 
     const ui::LogicalDisplayId displayId =
             getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index b24f2ff..30c58a5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -174,7 +174,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
     int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index b8911db..24efae8 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <optional>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <android/input.h>
@@ -241,10 +242,10 @@
         mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
     RawAbsoluteAxisInfo slotAxisInfo;
     deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
-    if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
-        ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
-              "properly.",
-              deviceContext.getName().c_str());
+    if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) {
+        LOG(WARNING) << "Touchpad " << deviceContext.getName()
+                     << " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly.";
+        slotAxisInfo.maxValue = 0;
     }
     mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
 
@@ -416,17 +417,17 @@
     mResettingInterpreter = false;
 }
 
-std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent& rawEvent) {
     if (mPointerCaptured) {
-        return mCapturedEventConverter.process(*rawEvent);
+        return mCapturedEventConverter.process(rawEvent);
     }
     if (mMotionAccumulator.getActiveSlotsCount() == 0) {
-        mGestureStartTime = rawEvent->when;
+        mGestureStartTime = rawEvent.when;
     }
     std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
     if (state) {
         updatePalmDetectionMetrics();
-        return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
+        return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
     } else {
         return {};
     }
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 546fa5b..8baa63e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -56,7 +56,7 @@
                                                     const InputReaderConfiguration& config,
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
     [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
 
     void consumeGesture(const Gesture* gesture);
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 8d78d0f..a3a48ef 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -36,7 +36,7 @@
     info.setVibrator(true);
 }
 
-std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent& rawEvent) {
     // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
     return {};
 }
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 9079c73..7519682 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
 
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
-    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
 
     [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
                                                 int32_t token) override;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 153236c..9e722d4 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,32 +47,32 @@
     mBtnTask = 0;
 }
 
-void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_KEY) {
-        switch (rawEvent->code) {
+void CursorButtonAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_KEY) {
+        switch (rawEvent.code) {
             case BTN_LEFT:
-                mBtnLeft = rawEvent->value;
+                mBtnLeft = rawEvent.value;
                 break;
             case BTN_RIGHT:
-                mBtnRight = rawEvent->value;
+                mBtnRight = rawEvent.value;
                 break;
             case BTN_MIDDLE:
-                mBtnMiddle = rawEvent->value;
+                mBtnMiddle = rawEvent.value;
                 break;
             case BTN_BACK:
-                mBtnBack = rawEvent->value;
+                mBtnBack = rawEvent.value;
                 break;
             case BTN_SIDE:
-                mBtnSide = rawEvent->value;
+                mBtnSide = rawEvent.value;
                 break;
             case BTN_FORWARD:
-                mBtnForward = rawEvent->value;
+                mBtnForward = rawEvent.value;
                 break;
             case BTN_EXTRA:
-                mBtnExtra = rawEvent->value;
+                mBtnExtra = rawEvent.value;
                 break;
             case BTN_TASK:
-                mBtnTask = rawEvent->value;
+                mBtnTask = rawEvent.value;
                 break;
         }
     }
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 6960644..256b2bb 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -29,7 +29,7 @@
     CursorButtonAccumulator();
     void reset(const InputDeviceContext& deviceContext);
 
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
 
     uint32_t getButtonState() const;
     inline bool isLeftPressed() const { return mBtnLeft; }
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
index 0714694..f85cab2 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -39,14 +39,14 @@
     mRelHWheel = 0;
 }
 
-void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_REL) {
-        switch (rawEvent->code) {
+void CursorScrollAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_REL) {
+        switch (rawEvent.code) {
             case REL_WHEEL:
-                mRelWheel = rawEvent->value;
+                mRelWheel = rawEvent.value;
                 break;
             case REL_HWHEEL:
-                mRelHWheel = rawEvent->value;
+                mRelHWheel = rawEvent.value;
                 break;
         }
     }
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index ae1b7a3..e563620 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -31,7 +31,7 @@
     void configure(InputDeviceContext& deviceContext);
     void reset(InputDeviceContext& deviceContext);
 
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
     void finishSync();
 
     inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b3f1700..4919068 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -45,12 +45,12 @@
     mCurrentSlot = -1;
 }
 
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_ABS) {
+void MultiTouchMotionAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_ABS) {
         bool newSlot = false;
         if (mUsingSlotsProtocol) {
-            if (rawEvent->code == ABS_MT_SLOT) {
-                mCurrentSlot = rawEvent->value;
+            if (rawEvent.code == ABS_MT_SLOT) {
+                mCurrentSlot = rawEvent.value;
                 newSlot = true;
             }
         } else if (mCurrentSlot < 0) {
@@ -72,12 +72,12 @@
             if (!mUsingSlotsProtocol) {
                 slot.mInUse = true;
             }
-            if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) {
-                warnIfNotInUse(*rawEvent, slot);
+            if (rawEvent.code == ABS_MT_POSITION_X || rawEvent.code == ABS_MT_POSITION_Y) {
+                warnIfNotInUse(rawEvent, slot);
             }
-            slot.populateAxisValue(rawEvent->code, rawEvent->value);
+            slot.populateAxisValue(rawEvent.code, rawEvent.value);
         }
-    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
+    } else if (rawEvent.type == EV_SYN && rawEvent.code == SYN_MT_REPORT) {
         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
         mCurrentSlot += 1;
     }
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index a0f2147..388ed82 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -76,7 +76,7 @@
     void configure(const InputDeviceContext& deviceContext, size_t slotCount,
                    bool usingSlotsProtocol);
     void reset(const InputDeviceContext& deviceContext);
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
     void finishSync();
 
     size_t getActiveSlotsCount() const;
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
index 27b8e40..2b82ddf 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -45,29 +45,29 @@
     mAbsTiltY = 0;
 }
 
-void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
-    if (rawEvent->type == EV_ABS) {
-        switch (rawEvent->code) {
+void SingleTouchMotionAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_ABS) {
+        switch (rawEvent.code) {
             case ABS_X:
-                mAbsX = rawEvent->value;
+                mAbsX = rawEvent.value;
                 break;
             case ABS_Y:
-                mAbsY = rawEvent->value;
+                mAbsY = rawEvent.value;
                 break;
             case ABS_PRESSURE:
-                mAbsPressure = rawEvent->value;
+                mAbsPressure = rawEvent.value;
                 break;
             case ABS_TOOL_WIDTH:
-                mAbsToolWidth = rawEvent->value;
+                mAbsToolWidth = rawEvent.value;
                 break;
             case ABS_DISTANCE:
-                mAbsDistance = rawEvent->value;
+                mAbsDistance = rawEvent.value;
                 break;
             case ABS_TILT_X:
-                mAbsTiltX = rawEvent->value;
+                mAbsTiltX = rawEvent.value;
                 break;
             case ABS_TILT_Y:
-                mAbsTiltY = rawEvent->value;
+                mAbsTiltY = rawEvent.value;
                 break;
         }
     }
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
index 93056f0..fb74bca 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -28,7 +28,7 @@
 public:
     SingleTouchMotionAccumulator();
 
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
     void reset(InputDeviceContext& deviceContext);
 
     inline int32_t getAbsoluteX() const { return mAbsX; }
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 8c4bed3..ba8577e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -52,60 +52,60 @@
     mHidUsageAccumulator.reset();
 }
 
-void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
-    mHidUsageAccumulator.process(*rawEvent);
+void TouchButtonAccumulator::process(const RawEvent& rawEvent) {
+    mHidUsageAccumulator.process(rawEvent);
 
-    if (rawEvent->type == EV_KEY) {
-        switch (rawEvent->code) {
+    if (rawEvent.type == EV_KEY) {
+        switch (rawEvent.code) {
             case BTN_TOUCH:
-                mBtnTouch = rawEvent->value;
+                mBtnTouch = rawEvent.value;
                 break;
             case BTN_STYLUS:
-                mBtnStylus = rawEvent->value;
+                mBtnStylus = rawEvent.value;
                 break;
             case BTN_STYLUS2:
             case BTN_0: // BTN_0 is what gets mapped for the HID usage
                         // Digitizers.SecondaryBarrelSwitch
-                mBtnStylus2 = rawEvent->value;
+                mBtnStylus2 = rawEvent.value;
                 break;
             case BTN_TOOL_FINGER:
-                mBtnToolFinger = rawEvent->value;
+                mBtnToolFinger = rawEvent.value;
                 break;
             case BTN_TOOL_PEN:
-                mBtnToolPen = rawEvent->value;
+                mBtnToolPen = rawEvent.value;
                 break;
             case BTN_TOOL_RUBBER:
-                mBtnToolRubber = rawEvent->value;
+                mBtnToolRubber = rawEvent.value;
                 break;
             case BTN_TOOL_BRUSH:
-                mBtnToolBrush = rawEvent->value;
+                mBtnToolBrush = rawEvent.value;
                 break;
             case BTN_TOOL_PENCIL:
-                mBtnToolPencil = rawEvent->value;
+                mBtnToolPencil = rawEvent.value;
                 break;
             case BTN_TOOL_AIRBRUSH:
-                mBtnToolAirbrush = rawEvent->value;
+                mBtnToolAirbrush = rawEvent.value;
                 break;
             case BTN_TOOL_MOUSE:
-                mBtnToolMouse = rawEvent->value;
+                mBtnToolMouse = rawEvent.value;
                 break;
             case BTN_TOOL_LENS:
-                mBtnToolLens = rawEvent->value;
+                mBtnToolLens = rawEvent.value;
                 break;
             case BTN_TOOL_DOUBLETAP:
-                mBtnToolDoubleTap = rawEvent->value;
+                mBtnToolDoubleTap = rawEvent.value;
                 break;
             case BTN_TOOL_TRIPLETAP:
-                mBtnToolTripleTap = rawEvent->value;
+                mBtnToolTripleTap = rawEvent.value;
                 break;
             case BTN_TOOL_QUADTAP:
-                mBtnToolQuadTap = rawEvent->value;
+                mBtnToolQuadTap = rawEvent.value;
                 break;
             case BTN_TOOL_QUINTTAP:
-                mBtnToolQuintTap = rawEvent->value;
+                mBtnToolQuintTap = rawEvent.value;
                 break;
             default:
-                processMappedKey(rawEvent->code, rawEvent->value);
+                processMappedKey(rawEvent.code, rawEvent.value);
         }
         return;
     }
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index e829692..c7adf84 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -33,7 +33,7 @@
     void configure();
     void reset();
 
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent& rawEvent);
 
     uint32_t getButtonState() const;
     ToolType getToolType() const;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index b89b7f3..6885adb 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -40,15 +40,15 @@
 }
 
 std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent(
-        const RawEvent* rawEvent) {
+        const RawEvent& rawEvent) {
     std::optional<SelfContainedHardwareState> out;
-    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        out = produceHardwareState(rawEvent->when);
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        out = produceHardwareState(rawEvent.when);
         mMotionAccumulator.finishSync();
         mMscTimestamp = 0;
     }
-    if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
-        mMscTimestamp = rawEvent->value;
+    if (rawEvent.type == EV_MSC && rawEvent.code == MSC_TIMESTAMP) {
+        mMscTimestamp = rawEvent.value;
     }
     mCursorButtonAccumulator.process(rawEvent);
     mMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 633448e..07e62c6 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -44,7 +44,7 @@
     HardwareStateConverter(const InputDeviceContext& deviceContext,
                            MultiTouchMotionAccumulator& motionAccumulator);
 
-    std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
+    std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent& event);
     void reset();
 
 private:
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index 69264f8..f4a5e0d 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -90,7 +90,7 @@
     // prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the
     // configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property.
     const std::string gesturePropPrefix = "gestureProp.";
-    for (const std::string key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
+    for (const std::string& key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
         std::string propertyName = key.substr(gesturePropPrefix.length());
         for (size_t i = 0; i < propertyName.length(); i++) {
             if (propertyName[i] == '_') {
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 255c7eb..5b7cc2d 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -47,6 +47,7 @@
         "liblog_rust",
         "liblogger",
         "libnix",
+        "libinput_rust",
     ],
     host_supported: true,
 }
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 6df339e..8b44af3 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -31,6 +31,7 @@
 use crate::input_filter_thread::InputFilterThread;
 use crate::slow_keys_filter::SlowKeysFilter;
 use crate::sticky_keys_filter::StickyKeysFilter;
+use input::ModifierState;
 use log::{error, info};
 use std::sync::{Arc, Mutex, RwLock};
 
@@ -169,12 +170,15 @@
         Self(callbacks)
     }
 
-    pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
-        let _ = self
-            .0
-            .read()
-            .unwrap()
-            .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
+    pub fn modifier_state_changed(
+        &self,
+        modifier_state: ModifierState,
+        locked_modifier_state: ModifierState,
+    ) {
+        let _ = self.0.read().unwrap().onModifierStateChanged(
+            modifier_state.bits() as i32,
+            locked_modifier_state.bits() as i32,
+        );
     }
 }
 
@@ -396,14 +400,15 @@
         IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback},
         KeyEvent::KeyEvent,
     };
+    use input::ModifierState;
     use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
     use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc, RwLock, RwLockWriteGuard};
     use std::time::Duration;
 
     #[derive(Default)]
     struct TestCallbacksInner {
-        last_modifier_state: u32,
-        last_locked_modifier_state: u32,
+        last_modifier_state: ModifierState,
+        last_locked_modifier_state: ModifierState,
         last_event: Option<KeyEvent>,
         test_thread: Option<FakeCppThread>,
     }
@@ -428,15 +433,15 @@
 
         pub fn clear(&mut self) {
             self.inner().last_event = None;
-            self.inner().last_modifier_state = 0;
-            self.inner().last_locked_modifier_state = 0;
+            self.inner().last_modifier_state = ModifierState::None;
+            self.inner().last_locked_modifier_state = ModifierState::None;
         }
 
-        pub fn get_last_modifier_state(&self) -> u32 {
+        pub fn get_last_modifier_state(&self) -> ModifierState {
             self.0.read().unwrap().last_modifier_state
         }
 
-        pub fn get_last_locked_modifier_state(&self) -> u32 {
+        pub fn get_last_locked_modifier_state(&self) -> ModifierState {
             self.0.read().unwrap().last_locked_modifier_state
         }
 
@@ -459,8 +464,10 @@
             modifier_state: i32,
             locked_modifier_state: i32,
         ) -> std::result::Result<(), binder::Status> {
-            self.inner().last_modifier_state = modifier_state as u32;
-            self.inner().last_locked_modifier_state = locked_modifier_state as u32;
+            self.inner().last_modifier_state =
+                ModifierState::from_bits(modifier_state as u32).unwrap();
+            self.inner().last_locked_modifier_state =
+                ModifierState::from_bits(locked_modifier_state as u32).unwrap();
             Result::Ok(())
         }
 
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 6c2277c..6c7c7fb 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -23,6 +23,7 @@
 use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
     DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
 };
+use input::ModifierState;
 use std::collections::HashSet;
 
 // Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
@@ -40,20 +41,6 @@
 const KEYCODE_FUNCTION: i32 = 119;
 const KEYCODE_NUM_LOCK: i32 = 143;
 
-// Modifier states: values are from /frameworks/native/include/android/input.h
-const META_ALT_ON: u32 = 0x02;
-const META_ALT_LEFT_ON: u32 = 0x10;
-const META_ALT_RIGHT_ON: u32 = 0x20;
-const META_SHIFT_ON: u32 = 0x01;
-const META_SHIFT_LEFT_ON: u32 = 0x40;
-const META_SHIFT_RIGHT_ON: u32 = 0x80;
-const META_CTRL_ON: u32 = 0x1000;
-const META_CTRL_LEFT_ON: u32 = 0x2000;
-const META_CTRL_RIGHT_ON: u32 = 0x4000;
-const META_META_ON: u32 = 0x10000;
-const META_META_LEFT_ON: u32 = 0x20000;
-const META_META_RIGHT_ON: u32 = 0x40000;
-
 pub struct StickyKeysFilter {
     next: Box<dyn Filter + Send + Sync>,
     listener: ModifierStateListener,
@@ -61,11 +48,11 @@
     contributing_devices: HashSet<i32>,
     /// State describing the current enabled modifiers. This contain both locked and non-locked
     /// modifier state bits.
-    modifier_state: u32,
+    modifier_state: ModifierState,
     /// State describing the current locked modifiers. These modifiers will not be cleared on a
     /// non-modifier key press. They will be cleared only if the locked modifier key is pressed
     /// again.
-    locked_modifier_state: u32,
+    locked_modifier_state: ModifierState,
 }
 
 impl StickyKeysFilter {
@@ -78,8 +65,8 @@
             next,
             listener,
             contributing_devices: HashSet::new(),
-            modifier_state: 0,
-            locked_modifier_state: 0,
+            modifier_state: ModifierState::None,
+            locked_modifier_state: ModifierState::None,
         }
     }
 }
@@ -93,12 +80,12 @@
             // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
             // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
             // the KeyEvent.
-            let old_modifier_state = event.metaState as u32;
+            let old_modifier_state = ModifierState::from_bits(event.metaState as u32).unwrap();
             let mut new_event = *event;
             // Send the current modifier state with the key event before clearing non-locked
             // modifier state
             new_event.metaState =
-                (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32;
+                (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state).bits() as i32;
             self.next.notify_key(&new_event);
             if up && !is_modifier_key(event.keyCode) {
                 modifier_state =
@@ -110,10 +97,10 @@
             // If ephemeral modifier key, capture the key and update the sticky modifier states
             let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
             let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
-            if locked_modifier_state & modifier_key_mask != 0 {
+            if locked_modifier_state & modifier_key_mask != ModifierState::None {
                 locked_modifier_state &= !symmetrical_modifier_key_mask;
                 modifier_state &= !symmetrical_modifier_key_mask;
-            } else if modifier_key_mask & modifier_state != 0 {
+            } else if modifier_key_mask & modifier_state != ModifierState::None {
                 locked_modifier_state |= modifier_key_mask;
                 modifier_state =
                     (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
@@ -134,11 +121,12 @@
         // Clear state if all contributing devices removed
         self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
         if self.contributing_devices.is_empty()
-            && (self.modifier_state != 0 || self.locked_modifier_state != 0)
+            && (self.modifier_state != ModifierState::None
+                || self.locked_modifier_state != ModifierState::None)
         {
-            self.modifier_state = 0;
-            self.locked_modifier_state = 0;
-            self.listener.modifier_state_changed(0, 0);
+            self.modifier_state = ModifierState::None;
+            self.locked_modifier_state = ModifierState::None;
+            self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
         }
         self.next.notify_devices_changed(device_infos);
     }
@@ -181,51 +169,53 @@
     )
 }
 
-fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 {
+fn get_ephemeral_modifier_key_mask(keycode: i32) -> ModifierState {
     match keycode {
-        KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON,
-        KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON,
-        KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON,
-        KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON,
-        KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON,
-        KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON,
-        KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON,
-        KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON,
-        _ => 0,
+        KEYCODE_ALT_LEFT => ModifierState::AltLeftOn | ModifierState::AltOn,
+        KEYCODE_ALT_RIGHT => ModifierState::AltRightOn | ModifierState::AltOn,
+        KEYCODE_SHIFT_LEFT => ModifierState::ShiftLeftOn | ModifierState::ShiftOn,
+        KEYCODE_SHIFT_RIGHT => ModifierState::ShiftRightOn | ModifierState::ShiftOn,
+        KEYCODE_CTRL_LEFT => ModifierState::CtrlLeftOn | ModifierState::CtrlOn,
+        KEYCODE_CTRL_RIGHT => ModifierState::CtrlRightOn | ModifierState::CtrlOn,
+        KEYCODE_META_LEFT => ModifierState::MetaLeftOn | ModifierState::MetaOn,
+        KEYCODE_META_RIGHT => ModifierState::MetaRightOn | ModifierState::MetaOn,
+        _ => ModifierState::None,
     }
 }
 
 /// Modifier mask including both left and right versions of a modifier key.
-fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 {
+fn get_symmetrical_modifier_key_mask(keycode: i32) -> ModifierState {
     match keycode {
-        KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON,
+        KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => {
+            ModifierState::AltLeftOn | ModifierState::AltRightOn | ModifierState::AltOn
+        }
         KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
-            META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON
+            ModifierState::ShiftLeftOn | ModifierState::ShiftRightOn | ModifierState::ShiftOn
         }
         KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
-            META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON
+            ModifierState::CtrlLeftOn | ModifierState::CtrlRightOn | ModifierState::CtrlOn
         }
         KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
-            META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON
+            ModifierState::MetaLeftOn | ModifierState::MetaRightOn | ModifierState::MetaOn
         }
-        _ => 0,
+        _ => ModifierState::None,
     }
 }
 
-fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 {
+fn clear_ephemeral_modifier_state(modifier_state: ModifierState) -> ModifierState {
     modifier_state
-        & !(META_ALT_LEFT_ON
-            | META_ALT_RIGHT_ON
-            | META_ALT_ON
-            | META_SHIFT_LEFT_ON
-            | META_SHIFT_RIGHT_ON
-            | META_SHIFT_ON
-            | META_CTRL_LEFT_ON
-            | META_CTRL_RIGHT_ON
-            | META_CTRL_ON
-            | META_META_LEFT_ON
-            | META_META_RIGHT_ON
-            | META_META_ON)
+        & !(ModifierState::AltLeftOn
+            | ModifierState::AltRightOn
+            | ModifierState::AltOn
+            | ModifierState::ShiftLeftOn
+            | ModifierState::ShiftRightOn
+            | ModifierState::ShiftOn
+            | ModifierState::CtrlLeftOn
+            | ModifierState::CtrlRightOn
+            | ModifierState::CtrlOn
+            | ModifierState::MetaLeftOn
+            | ModifierState::MetaRightOn
+            | ModifierState::MetaOn)
 }
 
 #[cfg(test)]
@@ -237,9 +227,7 @@
         StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
         KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
         KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
-        KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON,
-        META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON,
-        META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON,
+        KEYCODE_SHIFT_RIGHT, KEYCODE_SYM,
     };
     use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
     use binder::Strong;
@@ -247,6 +235,7 @@
         DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
         KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
     };
+    use input::ModifierState;
     use std::sync::{Arc, RwLock};
 
     static DEVICE_ID: i32 = 1;
@@ -347,30 +336,30 @@
             Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
         );
         let test_states = &[
-            (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON),
-            (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON),
-            (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON),
-            (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON),
-            (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON),
-            (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON),
-            (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON),
-            (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON),
+            (KEYCODE_ALT_LEFT, ModifierState::AltOn | ModifierState::AltLeftOn),
+            (KEYCODE_ALT_RIGHT, ModifierState::AltOn | ModifierState::AltRightOn),
+            (KEYCODE_CTRL_LEFT, ModifierState::CtrlOn | ModifierState::CtrlLeftOn),
+            (KEYCODE_CTRL_RIGHT, ModifierState::CtrlOn | ModifierState::CtrlRightOn),
+            (KEYCODE_SHIFT_LEFT, ModifierState::ShiftOn | ModifierState::ShiftLeftOn),
+            (KEYCODE_SHIFT_RIGHT, ModifierState::ShiftOn | ModifierState::ShiftRightOn),
+            (KEYCODE_META_LEFT, ModifierState::MetaOn | ModifierState::MetaLeftOn),
+            (KEYCODE_META_RIGHT, ModifierState::MetaOn | ModifierState::MetaRightOn),
         ];
         for test_state in test_states.iter() {
             test_filter.clear();
             test_callbacks.clear();
             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
-            assert_eq!(test_callbacks.get_last_modifier_state(), 0);
-            assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+            assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+            assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
 
             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
-            assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+            assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
 
             // Re-send keys to lock it
             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
-            assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+            assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
 
             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
             assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
@@ -382,8 +371,8 @@
             assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
 
             sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
-            assert_eq!(test_callbacks.get_last_modifier_state(), 0);
-            assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+            assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+            assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
         }
     }
 
@@ -398,14 +387,17 @@
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
 
-        assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
-        assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+        assert_eq!(
+            test_callbacks.get_last_modifier_state(),
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+        );
+        assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
 
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
 
-        assert_eq!(test_callbacks.get_last_modifier_state(), 0);
-        assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+        assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+        assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
     }
 
     #[test]
@@ -427,20 +419,26 @@
 
         assert_eq!(
             test_callbacks.get_last_modifier_state(),
-            META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON
+            ModifierState::ShiftLeftOn
+                | ModifierState::ShiftOn
+                | ModifierState::CtrlLeftOn
+                | ModifierState::CtrlOn
         );
         assert_eq!(
             test_callbacks.get_last_locked_modifier_state(),
-            META_CTRL_LEFT_ON | META_CTRL_ON
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
         );
 
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
 
-        assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+        assert_eq!(
+            test_callbacks.get_last_modifier_state(),
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+        );
         assert_eq!(
             test_callbacks.get_last_locked_modifier_state(),
-            META_CTRL_LEFT_ON | META_CTRL_ON
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
         );
     }
 
@@ -458,13 +456,13 @@
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
         assert_eq!(
             test_filter.last_event().unwrap().metaState as u32,
-            META_CTRL_LEFT_ON | META_CTRL_ON
+            (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
         );
 
         sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
         assert_eq!(
             test_filter.last_event().unwrap().metaState as u32,
-            META_CTRL_LEFT_ON | META_CTRL_ON
+            (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
         );
     }
 
@@ -499,15 +497,18 @@
         });
 
         sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
-        assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+        assert_eq!(
+            test_callbacks.get_last_modifier_state(),
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+        );
         assert_eq!(
             test_callbacks.get_last_locked_modifier_state(),
-            META_CTRL_LEFT_ON | META_CTRL_ON
+            ModifierState::CtrlLeftOn | ModifierState::CtrlOn
         );
 
         sticky_keys_filter.notify_devices_changed(&[]);
-        assert_eq!(test_callbacks.get_last_modifier_state(), 0);
-        assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+        assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+        assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
     }
 
     fn setup_filter(
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index 530416c..e17ee3a 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -215,6 +215,10 @@
     mStaleEventTimeout = timeout;
 }
 
+void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
+    mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+}
+
 void FakeInputDispatcherPolicy::assertUserActivityNotPoked() {
     std::unique_lock lock(mLock);
     base::ScopedLockAssertion assumeLocked(mLock);
@@ -401,6 +405,9 @@
 
 nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&,
                                                                  const KeyEvent&, uint32_t) {
+    if (mConsumeKeyBeforeDispatching) {
+        return -1;
+    }
     nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
     // Clear intercept state so we could dispatch the event in next wake.
     mInterceptKeyTimeout = 0ms;
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index 2c86146..62ff10f 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -115,6 +115,7 @@
     void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
     void assertUnhandledKeyReported(int32_t keycode);
     void assertUnhandledKeyNotReported();
+    void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
 
 private:
     std::mutex mLock;
@@ -144,6 +145,8 @@
 
     std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
 
+    bool mConsumeKeyBeforeDispatching = false;
+
     BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
 
     std::condition_variable mNotifyUnhandledKey;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 2bb57b3..d0998ba 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -76,13 +76,15 @@
     mCustomIconStyle = icon.style;
 }
 
-void FakePointerController::setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) {
-    if (skip) {
-        mDisplaysToSkipScreenshot.insert(displayId);
-    } else {
-        mDisplaysToSkipScreenshot.erase(displayId);
-    }
-};
+void FakePointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) {
+    mDisplaysToSkipScreenshotFlagChanged = true;
+    mDisplaysToSkipScreenshot.insert(displayId);
+}
+
+void FakePointerController::clearSkipScreenshotFlags() {
+    mDisplaysToSkipScreenshotFlagChanged = true;
+    mDisplaysToSkipScreenshot.clear();
+}
 
 void FakePointerController::assertViewportSet(ui::LogicalDisplayId displayId) {
     ASSERT_TRUE(mDisplayId);
@@ -125,13 +127,21 @@
     ASSERT_EQ(std::nullopt, mCustomIconStyle);
 }
 
-void FakePointerController::assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId,
-                                                             bool isHidden) {
-    if (isHidden) {
-        ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end());
-    } else {
-        ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end());
-    }
+void FakePointerController::assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId) {
+    ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end());
+}
+
+void FakePointerController::assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId) {
+    ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end());
+}
+
+void FakePointerController::assertSkipScreenshotFlagChanged() {
+    ASSERT_TRUE(mDisplaysToSkipScreenshotFlagChanged);
+    mDisplaysToSkipScreenshotFlagChanged = false;
+}
+
+void FakePointerController::assertSkipScreenshotFlagNotChanged() {
+    ASSERT_FALSE(mDisplaysToSkipScreenshotFlagChanged);
 }
 
 bool FakePointerController::isPointerShown() {
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 5bc713f..2c76c62 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -45,7 +45,8 @@
     void setDisplayViewport(const DisplayViewport& viewport) override;
     void updatePointerIcon(PointerIconStyle iconId) override;
     void setCustomPointerIcon(const SpriteIcon& icon) override;
-    void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) override;
+    void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
+    void clearSkipScreenshotFlags() override;
     void fade(Transition) override;
 
     void assertViewportSet(ui::LogicalDisplayId displayId);
@@ -56,7 +57,10 @@
     void assertPointerIconNotSet();
     void assertCustomPointerIconSet(PointerIconStyle iconId);
     void assertCustomPointerIconNotSet();
-    void assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId, bool isHidden);
+    void assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId);
+    void assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId);
+    void assertSkipScreenshotFlagChanged();
+    void assertSkipScreenshotFlagNotChanged();
     bool isPointerShown();
 
 private:
@@ -80,6 +84,7 @@
 
     std::map<ui::LogicalDisplayId, std::vector<int32_t>> mSpotsByDisplay;
     std::unordered_set<ui::LogicalDisplayId> mDisplaysToSkipScreenshot;
+    bool mDisplaysToSkipScreenshotFlagChanged{false};
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index ff9bd9e..34c81fc 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -81,7 +81,7 @@
         event.type = type;
         event.code = code;
         event.value = value;
-        std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(&event);
+        std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(event);
         EXPECT_FALSE(schs.has_value());
     }
 
@@ -93,7 +93,7 @@
         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
-        return mConverter->processRawEvent(&event);
+        return mConverter->processRawEvent(event);
     }
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8de28c6..56a05a3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -610,28 +610,6 @@
     return args;
 }
 
-static NotifyKeyArgs generateSystemShortcutArgs(
-        int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) {
-    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    // Define a valid key event.
-    NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
-                       AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C,
-                       AMETA_META_ON, currentTime);
-
-    return args;
-}
-
-static NotifyKeyArgs generateAssistantKeyArgs(
-        int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) {
-    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    // Define a valid key event.
-    NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
-                       AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST,
-                       KEY_ASSISTANT, AMETA_NONE, currentTime);
-
-    return args;
-}
-
 [[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
                                                          ui::LogicalDisplayId displayId,
                                                          const std::vector<PointF>& points) {
@@ -6628,17 +6606,18 @@
 
     window->consumeFocusEvent(true);
 
-    mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
 
     // Window should receive key down event.
     window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
 
-    // Should have poked user activity
+    // Should have not poked user activity
     mDispatcher->waitForIdle();
     mFakePolicy->assertUserActivityNotPoked();
 }
 
-TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) {
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyConsumedKey) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
@@ -6650,41 +6629,20 @@
 
     window->consumeFocusEvent(true);
 
+    mFakePolicy->setConsumeKeyBeforeDispatching(true);
+
     mDispatcher->notifyKey(
-            generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
     mDispatcher->waitForIdle();
 
-    // System key is not passed down
+    // Key is not passed down
     window->assertNoEvents();
 
     // Should have poked user activity
     mFakePolicy->assertUserActivityPoked();
 }
 
-TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) {
-    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window =
-            sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
-                                       ui::LogicalDisplayId::DEFAULT);
-
-    window->setFocusable(true);
-    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
-    setFocusedWindow(window);
-
-    window->consumeFocusEvent(true);
-
-    mDispatcher->notifyKey(
-            generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
-    mDispatcher->waitForIdle();
-
-    // System key is not passed down
-    window->assertNoEvents();
-
-    // Should have poked user activity
-    mFakePolicy->assertUserActivityPoked();
-}
-
-TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) {
+TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserActivity) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
@@ -6697,8 +6655,10 @@
 
     window->consumeFocusEvent(true);
 
+    mFakePolicy->setConsumeKeyBeforeDispatching(true);
+
     mDispatcher->notifyKey(
-            generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
     mDispatcher->waitForIdle();
 
     // System key is not passed down
@@ -6708,6 +6668,39 @@
     mFakePolicy->assertUserActivityPoked();
 }
 
+class DisableUserActivityInputDispatcherTest : public InputDispatcherTest,
+                                               public ::testing::WithParamInterface<bool> {};
+
+TEST_P(DisableUserActivityInputDispatcherTest, NotPassedToUserUserActivity) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+                                       ui::LogicalDisplayId::DEFAULT);
+
+    window->setDisableUserActivity(GetParam());
+
+    window->setFocusable(true);
+    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+    setFocusedWindow(window);
+
+    window->consumeFocusEvent(true);
+
+    mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+                                   .keyCode(AKEYCODE_A)
+                                   .policyFlags(0)
+                                   .build());
+    mDispatcher->waitForIdle();
+
+    // Key is not passed down
+    window->assertNoEvents();
+
+    // Should not have poked user activity
+    mFakePolicy->assertUserActivityNotPoked();
+}
+
+INSTANTIATE_TEST_CASE_P(DisableUserActivity, DisableUserActivityInputDispatcherTest,
+                        ::testing::Bool());
+
 TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -11054,6 +11047,37 @@
     mWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) {
+    // The default display is the focused display to begin with.
+    requestAndVerifyPointerCapture(mWindow, true);
+
+    // Move the second window to a second display, make it the focused window on that display.
+    mSecondWindow->editInfo()->displayId = SECOND_DISPLAY_ID;
+    mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+    setFocusedWindow(mSecondWindow);
+    mSecondWindow->consumeFocusEvent(true);
+
+    mWindow->assertNoEvents();
+
+    // The second window cannot gain capture because it is not on the focused display.
+    mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+    mFakePolicy->assertSetPointerCaptureNotCalled();
+    mSecondWindow->assertNoEvents();
+
+    // Make the second display the focused display.
+    mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+
+    // This causes the first window to lose pointer capture, and it's unable to request capture.
+    mWindow->consumeCaptureEvent(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
+
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    mFakePolicy->assertSetPointerCaptureNotCalled();
+
+    // The second window is now able to gain pointer capture successfully.
+    requestAndVerifyPointerCapture(mSecondWindow, true);
+}
+
 using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests;
 
 TEST_F(InputDispatcherPointerCaptureDeathTest,
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index fea1349..b5c9232 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -104,7 +104,7 @@
     event.type = type;
     event.code = code;
     event.value = value;
-    return mMapper->process(&event);
+    return mMapper->process(event);
 }
 
 const char* InputMapperTest::DEVICE_NAME = "device";
@@ -195,7 +195,7 @@
     event.type = type;
     event.code = code;
     event.value = value;
-    std::list<NotifyArgs> processArgList = mapper.process(&event);
+    std::list<NotifyArgs> processArgList = mapper.process(event);
     for (const NotifyArgs& args : processArgList) {
         mFakeListener->notify(args);
     }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8536ff0..fe238f3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -309,9 +309,9 @@
         return {};
     }
 
-    std::list<NotifyArgs> process(const RawEvent* rawEvent) override {
+    std::list<NotifyArgs> process(const RawEvent& rawEvent) override {
         std::scoped_lock<std::mutex> lock(mLock);
-        mLastEvent = *rawEvent;
+        mLastEvent = rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
         return mProcessResult;
@@ -3314,6 +3314,10 @@
 
 class KeyboardInputMapperTest : public InputMapperTest {
 protected:
+    void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+                               InputDeviceClass::ALPHAKEY);
+    }
     const std::string UNIQUE_ID = "local:0";
     const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
     void prepareDisplay(ui::Rotation orientation);
@@ -3354,8 +3358,7 @@
 
 TEST_F(KeyboardInputMapperTest, GetSources) {
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
 }
@@ -3370,8 +3373,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
@@ -3471,8 +3473,7 @@
     mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
@@ -3493,8 +3494,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     NotifyKeyArgs args;
 
     // Key down
@@ -3516,8 +3516,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3557,8 +3556,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -3579,8 +3577,7 @@
 
     addConfigurationProperty("keyboard.orientationAware", "1");
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     prepareDisplay(ui::ROTATION_0);
     ASSERT_NO_FATAL_FAILURE(
@@ -3651,8 +3648,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     NotifyKeyArgs args;
 
     // Display id should be LogicalDisplayId::INVALID without any display configuration.
@@ -3677,8 +3673,7 @@
 
     addConfigurationProperty("keyboard.orientationAware", "1");
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     NotifyKeyArgs args;
 
     // Display id should be LogicalDisplayId::INVALID without any display configuration.
@@ -3705,8 +3700,7 @@
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
     ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
@@ -3717,8 +3711,7 @@
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) {
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z);
     ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y))
@@ -3730,8 +3723,7 @@
 
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
     ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
@@ -3742,8 +3734,7 @@
 
 TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
 
@@ -3762,8 +3753,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
@@ -3828,8 +3818,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     // Meta state should be AMETA_NONE after reset
     std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
@@ -3878,16 +3867,14 @@
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
     KeyboardInputMapper& mapper2 =
             device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
                                                                 mFakePolicy
                                                                         ->getReaderConfiguration(),
-                                                                AINPUT_SOURCE_KEYBOARD,
-                                                                AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+                                                                AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                /*changes=*/{});
@@ -3949,8 +3936,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
@@ -4000,8 +3986,7 @@
             device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
                                                                 mFakePolicy
                                                                         ->getReaderConfiguration(),
-                                                                AINPUT_SOURCE_KEYBOARD,
-                                                                AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+                                                                AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                /*changes=*/{});
@@ -4020,11 +4005,9 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     // Suppose we have two mappers. (DPAD + KEYBOARD)
-    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD,
-                                               AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
@@ -4042,8 +4025,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper1 =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     // keyboard 2.
     const std::string USB2 = "USB2";
@@ -4065,8 +4047,7 @@
             device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
                                                                 mFakePolicy
                                                                         ->getReaderConfiguration(),
-                                                                AINPUT_SOURCE_KEYBOARD,
-                                                                AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+                                                                AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                /*changes=*/{});
@@ -4122,8 +4103,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
@@ -4148,8 +4128,7 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
-    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                               AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     std::list<NotifyArgs> unused =
             mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                /*changes=*/{});
@@ -4180,8 +4159,7 @@
                                     RawLayoutInfo{.languageTag = "en", .layoutType = "extended"});
 
     // Configuration
-    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                               AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     InputReaderConfiguration config;
     std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
 
@@ -4192,8 +4170,7 @@
 TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
     NotifyKeyArgs args;
 
     // Key down
@@ -4202,14 +4179,27 @@
     ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
 }
 
-// --- KeyboardInputMapperTest_ExternalDevice ---
+// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
 
-class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
+class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest {
 protected:
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
+    void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+                               InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL);
+    }
 };
 
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) {
+// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest {
+protected:
+    void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+                               InputDeviceClass::EXTERNAL);
+    }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
     // For external devices, keys will trigger wake on key down. Media keys should also trigger
     // wake if triggered from external devices.
 
@@ -4219,8 +4209,7 @@
                           POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
@@ -4248,7 +4237,7 @@
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
 
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) {
+TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
     // For external devices, keys will trigger wake on key down. Media keys should not trigger
     // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
 
@@ -4257,8 +4246,7 @@
                           POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
     NotifyKeyArgs args;
@@ -4278,7 +4266,7 @@
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
 
-TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
     // Tv Remote key's wake behavior is prescribed by the keylayout file.
 
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
@@ -4287,8 +4275,7 @@
 
     addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
     KeyboardInputMapper& mapper =
-            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
-                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
 
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
@@ -5485,6 +5472,9 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             x, y, pressure, size, tool, tool, tool, tool, orientation, distance));
     ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT));
+    ASSERT_EQ(args.flags,
+              AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+                      AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION);
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
@@ -7927,6 +7917,7 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor,
             orientation, distance));
+    ASSERT_EQ(args.flags, AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
@@ -10595,24 +10586,24 @@
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
 }
 
-TEST_F(LightControllerTest, PlayerIdLight) {
+TEST_F(LightControllerTest, SonyPlayerIdLight) {
     RawLightInfo info1 = {.id = 1,
-                          .name = "player1",
+                          .name = "sony1",
                           .maxBrightness = 255,
                           .flags = InputLightClass::BRIGHTNESS,
                           .path = ""};
     RawLightInfo info2 = {.id = 2,
-                          .name = "player2",
+                          .name = "sony2",
                           .maxBrightness = 255,
                           .flags = InputLightClass::BRIGHTNESS,
                           .path = ""};
     RawLightInfo info3 = {.id = 3,
-                          .name = "player3",
+                          .name = "sony3",
                           .maxBrightness = 255,
                           .flags = InputLightClass::BRIGHTNESS,
                           .path = ""};
     RawLightInfo info4 = {.id = 4,
-                          .name = "player4",
+                          .name = "sony4",
                           .maxBrightness = 255,
                           .flags = InputLightClass::BRIGHTNESS,
                           .path = ""};
@@ -10626,6 +10617,49 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
+    ASSERT_STREQ("sony", lights[0].name.c_str());
+    ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
+    ASSERT_EQ(controller.getLightPlayerId(lights[0].id).value_or(-1), LIGHT_PLAYER_ID);
+    ASSERT_STREQ("sony", lights[0].name.c_str());
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+    RawLightInfo info1 = {.id = 1,
+                          .name = "player-1",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info2 = {.id = 2,
+                          .name = "player-2",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info3 = {.id = 3,
+                          .name = "player-3",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info4 = {.id = 4,
+                          .name = "player-4",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+    mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+    mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+    mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_STREQ("player", lights[0].name.c_str());
     ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
     ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
     ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
diff --git a/services/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp
index 32acb5f..a9d370a 100644
--- a/services/inputflinger/tests/InputTraceSession.cpp
+++ b/services/inputflinger/tests/InputTraceSession.cpp
@@ -20,6 +20,9 @@
 #include <android-base/logging.h>
 #include <gtest/gtest.h>
 #include <input/PrintTools.h>
+#include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h>
 
 #include <utility>
 
@@ -30,6 +33,8 @@
 using perfetto::protos::pbzero::AndroidKeyEvent;
 using perfetto::protos::pbzero::AndroidMotionEvent;
 using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent;
+using perfetto::protos::pbzero::WinscopeExtensions;
+using perfetto::protos::pbzero::WinscopeExtensionsImpl;
 
 // These operator<< definitions must be in the global namespace for them to be accessible to the
 // GTEST library. They cannot be in the anonymous namespace.
@@ -85,38 +90,45 @@
 
     Trace::Decoder trace{rawTrace};
     if (trace.has_packet()) {
-        auto it = trace.packet();
-        while (it) {
+        for (auto it = trace.packet(); it; it++) {
             TracePacket::Decoder packet{it->as_bytes()};
-            if (packet.has_android_input_event()) {
-                AndroidInputEvent::Decoder event{packet.android_input_event()};
-                if (event.has_dispatcher_motion_event()) {
-                    tracedMotions.emplace_back(event.dispatcher_motion_event(),
-                                               /*redacted=*/false);
-                }
-                if (event.has_dispatcher_motion_event_redacted()) {
-                    tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(),
-                                               /*redacted=*/true);
-                }
-                if (event.has_dispatcher_key_event()) {
-                    tracedKeys.emplace_back(event.dispatcher_key_event(),
-                                            /*redacted=*/false);
-                }
-                if (event.has_dispatcher_key_event_redacted()) {
-                    tracedKeys.emplace_back(event.dispatcher_key_event_redacted(),
-                                            /*redacted=*/true);
-                }
-                if (event.has_dispatcher_window_dispatch_event()) {
-                    tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(),
-                                                        /*redacted=*/false);
-                }
-                if (event.has_dispatcher_window_dispatch_event_redacted()) {
-                    tracedWindowDispatches
-                            .emplace_back(event.dispatcher_window_dispatch_event_redacted(),
-                                          /*redacted=*/true);
-                }
+            if (!packet.has_winscope_extensions()) {
+                continue;
             }
-            it++;
+
+            WinscopeExtensions::Decoder extensions{packet.winscope_extensions()};
+            const auto& field =
+                    extensions.Get(WinscopeExtensionsImpl::kAndroidInputEventFieldNumber);
+            if (!field.valid()) {
+                continue;
+            }
+
+            AndroidInputEvent::Decoder event{field.as_bytes()};
+            if (event.has_dispatcher_motion_event()) {
+                tracedMotions.emplace_back(event.dispatcher_motion_event(),
+                                           /*redacted=*/false);
+            }
+            if (event.has_dispatcher_motion_event_redacted()) {
+                tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(),
+                                           /*redacted=*/true);
+            }
+            if (event.has_dispatcher_key_event()) {
+                tracedKeys.emplace_back(event.dispatcher_key_event(),
+                                        /*redacted=*/false);
+            }
+            if (event.has_dispatcher_key_event_redacted()) {
+                tracedKeys.emplace_back(event.dispatcher_key_event_redacted(),
+                                        /*redacted=*/true);
+            }
+            if (event.has_dispatcher_window_dispatch_event()) {
+                tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(),
+                                                    /*redacted=*/false);
+            }
+            if (event.has_dispatcher_window_dispatch_event_redacted()) {
+                tracedWindowDispatches
+                        .emplace_back(event.dispatcher_window_dispatch_event_redacted(),
+                                      /*redacted=*/true);
+            }
         }
     }
     return std::tuple{std::move(tracedMotions), std::move(tracedKeys),
diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h
index ed20bc8..bda5521 100644
--- a/services/inputflinger/tests/InputTraceSession.h
+++ b/services/inputflinger/tests/InputTraceSession.h
@@ -22,7 +22,6 @@
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <perfetto/config/android/android_input_event_config.pbzero.h>
-#include <perfetto/trace/android/android_input_event.pbzero.h>
 #include <perfetto/trace/trace.pbzero.h>
 #include <perfetto/tracing.h>
 #include <variant>
diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp
index 617d67f..2ccd93e 100644
--- a/services/inputflinger/tests/InputTracingTest.cpp
+++ b/services/inputflinger/tests/InputTracingTest.cpp
@@ -30,6 +30,8 @@
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h>
 #include <perfetto/trace/trace.pbzero.h>
 #include <private/android_filesystem_config.h>
 #include <map>
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 6389cdc..4441724 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -27,7 +27,9 @@
 
 #include <EventHub.h>
 #include <InputReaderBase.h>
+#include <InputReaderContext.h>
 #include <NotifyArgs.h>
+#include <PointerChoreographerPolicyInterface.h>
 #include <StylusState.h>
 #include <VibrationElement.h>
 #include <android-base/logging.h>
@@ -36,6 +38,7 @@
 #include <input/InputDevice.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
+#include <input/KeyboardClassifier.h>
 #include <input/PropertyMap.h>
 #include <input/TouchVideoFrame.h>
 #include <input/VirtualKeyMap.h>
@@ -75,8 +78,11 @@
     MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when));
     MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ());
 
+    KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; };
+
 private:
     int32_t mGeneration = 0;
+    std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>();
 };
 
 class MockEventHubInterface : public EventHubInterface {
@@ -174,4 +180,12 @@
     MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
 };
 
+class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolicyInterface {
+public:
+    MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, createPointerController,
+                (PointerControllerInterface::ControllerType), (override));
+    MOCK_METHOD(void, notifyPointerDisplayIdChanged,
+                (ui::LogicalDisplayId displayId, const FloatPoint& position), (override));
+};
+
 } // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 031b77d..ada841d 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -67,8 +67,7 @@
         EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
 
         mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
-                                                         AINPUT_SOURCE_KEYBOARD,
-                                                         AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+                                                         AINPUT_SOURCE_KEYBOARD);
     }
 
     void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) {
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
index 5e67506..b441a23 100644
--- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -38,7 +38,7 @@
         event.type = type;
         event.code = code;
         event.value = value;
-        mMotionAccumulator.process(&event);
+        mMotionAccumulator.process(event);
     }
 };
 
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1689b33..3f2d6ec 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "FakePointerController.h"
+#include "InterfaceMocks.h"
 #include "NotifyArgsBuilders.h"
 #include "TestEventMatchers.h"
 #include "TestInputListener.h"
@@ -89,10 +90,55 @@
 
 // --- PointerChoreographerTest ---
 
-class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
+class TestPointerChoreographer : public PointerChoreographer {
+public:
+    TestPointerChoreographer(InputListenerInterface& inputListener,
+                             PointerChoreographerPolicyInterface& policy,
+                             sp<gui::WindowInfosListener>& windowInfoListener,
+                             const std::vector<gui::WindowInfo>& mInitialWindowInfos);
+};
+
+TestPointerChoreographer::TestPointerChoreographer(
+        InputListenerInterface& inputListener, PointerChoreographerPolicyInterface& policy,
+        sp<gui::WindowInfosListener>& windowInfoListener,
+        const std::vector<gui::WindowInfo>& mInitialWindowInfos)
+      : PointerChoreographer(
+                inputListener, policy,
+                [&windowInfoListener,
+                 &mInitialWindowInfos](const sp<android::gui::WindowInfosListener>& listener) {
+                    windowInfoListener = listener;
+                    return mInitialWindowInfos;
+                },
+                [&windowInfoListener](const sp<android::gui::WindowInfosListener>& listener) {
+                    windowInfoListener = nullptr;
+                }) {}
+
+class PointerChoreographerTest : public testing::Test {
 protected:
     TestInputListener mTestListener;
-    PointerChoreographer mChoreographer{mTestListener, *this};
+    sp<gui::WindowInfosListener> mRegisteredWindowInfoListener;
+    std::vector<gui::WindowInfo> mInjectedInitialWindowInfos;
+    testing::NiceMock<MockPointerChoreographerPolicyInterface> mMockPolicy;
+    TestPointerChoreographer mChoreographer{mTestListener, mMockPolicy,
+                                            mRegisteredWindowInfoListener,
+                                            mInjectedInitialWindowInfos};
+
+    void SetUp() override {
+        // flag overrides
+        input_flags::hide_pointer_indicators_for_secure_windows(true);
+
+        ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
+            std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+            EXPECT_FALSE(pc->isPointerShown());
+            mCreatedControllers.emplace_back(type, pc);
+            return pc;
+        });
+
+        ON_CALL(mMockPolicy, notifyPointerDisplayIdChanged)
+                .WillByDefault([this](ui::LogicalDisplayId displayId, const FloatPoint& position) {
+                    mPointerDisplayIdNotified = displayId;
+                });
+    }
 
     std::shared_ptr<FakePointerController> assertPointerControllerCreated(
             ControllerType expectedType) {
@@ -131,23 +177,20 @@
 
     void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
 
+    void assertWindowInfosListenerRegistered() {
+        ASSERT_NE(nullptr, mRegisteredWindowInfoListener)
+                << "WindowInfosListener was not registered";
+    }
+
+    void assertWindowInfosListenerNotRegistered() {
+        ASSERT_EQ(nullptr, mRegisteredWindowInfoListener)
+                << "WindowInfosListener was not unregistered";
+    }
+
 private:
     std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
             mCreatedControllers;
     std::optional<ui::LogicalDisplayId> mPointerDisplayIdNotified;
-
-    std::shared_ptr<PointerControllerInterface> createPointerController(
-            ControllerType type) override {
-        std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
-        EXPECT_FALSE(pc->isPointerShown());
-        mCreatedControllers.emplace_back(type, pc);
-        return pc;
-    }
-
-    void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
-                                       const FloatPoint& position) override {
-        mPointerDisplayIdNotified = displayId;
-    }
 };
 
 TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -1636,74 +1679,252 @@
     firstMousePc->assertPointerIconNotSet();
 }
 
-TEST_F_WITH_FLAGS(PointerChoreographerTest, HidesTouchSpotsOnMirroredDisplaysForSecureWindow,
-                  REQUIRES_FLAGS_ENABLED(
-                          ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
-    // Add a touch device and enable show touches.
-    mChoreographer.notifyInputDevicesChanged(
-            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
-    mChoreographer.setShowTouchesEnabled(true);
+using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
+        std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
+                   std::function<void(PointerChoreographer&)>, int32_t /*action*/>;
 
-    // Emit touch events to create PointerController
-    mChoreographer.notifyMotion(
-            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                    .pointer(FIRST_TOUCH_POINTER)
-                    .deviceId(DEVICE_ID)
-                    .displayId(DISPLAY_ID)
-                    .build());
+class SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture
+      : public PointerChoreographerTest,
+        public ::testing::WithParamInterface<
+                SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam> {
+protected:
+    void initializePointerDevice(const PointerBuilder& pointerBuilder, const uint32_t source,
+                                 const std::function<void(PointerChoreographer&)> onControllerInit,
+                                 const int32_t action) {
+        mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
 
-    // By default touch indicators should not be hidden
-    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
-    pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
-    pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+        // Add appropriate pointer device
+        mChoreographer.notifyInputDevicesChanged(
+                {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
+        onControllerInit(mChoreographer);
 
-    // adding secure window on display should set flag to hide pointer indicators on corresponding
-    // mirrored display
+        // Emit input events to create PointerController
+        mChoreographer.notifyMotion(MotionArgsBuilder(action, source)
+                                            .pointer(pointerBuilder)
+                                            .deviceId(DEVICE_ID)
+                                            .displayId(DISPLAY_ID)
+                                            .build());
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+        PointerChoreographerTest, SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+        ::testing::Values(
+                std::make_tuple(
+                        "TouchSpots", AINPUT_SOURCE_TOUCHSCREEN, ControllerType::TOUCH,
+                        FIRST_TOUCH_POINTER,
+                        [](PointerChoreographer& pc) { pc.setShowTouchesEnabled(true); },
+                        AMOTION_EVENT_ACTION_DOWN),
+                std::make_tuple(
+                        "Mouse", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, MOUSE_POINTER,
+                        [](PointerChoreographer& pc) {}, AMOTION_EVENT_ACTION_DOWN),
+                std::make_tuple(
+                        "Stylus", AINPUT_SOURCE_STYLUS, ControllerType::STYLUS, STYLUS_POINTER,
+                        [](PointerChoreographer& pc) { pc.setStylusPointerIconEnabled(true); },
+                        AMOTION_EVENT_ACTION_HOVER_ENTER),
+                std::make_tuple(
+                        "DrawingTablet", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS,
+                        ControllerType::MOUSE, STYLUS_POINTER, [](PointerChoreographer& pc) {},
+                        AMOTION_EVENT_ACTION_HOVER_ENTER)),
+        [](const testing::TestParamInfo<
+                SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam>& p) {
+            return std::string{std::get<0>(p.param)};
+        });
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+       WindowInfosListenerIsOnlyRegisteredWhenRequired) {
+    const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+            GetParam();
+    assertWindowInfosListenerNotRegistered();
+
+    // Listener should registered when a pointer device is added
+    initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+    assertWindowInfosListenerRegistered();
+
+    mChoreographer.notifyInputDevicesChanged({});
+    assertWindowInfosListenerNotRegistered();
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+       InitialDisplayInfoIsPopulatedForListener) {
+    const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+            GetParam();
+    // listener should not be registered if there is no pointer device
+    assertWindowInfosListenerNotRegistered();
+
     gui::WindowInfo windowInfo;
     windowInfo.displayId = DISPLAY_ID;
     windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
-    mChoreographer.onWindowInfosChanged({windowInfo});
-    pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/true);
-    pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+    mInjectedInitialWindowInfos = {windowInfo};
 
-    // removing the secure window should reset the state
-    windowInfo.inputConfig.clear(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY);
-    mChoreographer.onWindowInfosChanged({windowInfo});
-    pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
-    pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+    initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+    assertWindowInfosListenerRegistered();
+
+    // Pointer indicators should be hidden based on the initial display info
+    auto pc = assertPointerControllerCreated(controllerType);
+    pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+    // un-marking the privacy sensitive display should reset the state
+    windowInfo.inputConfig.clear();
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
 }
 
-TEST_F_WITH_FLAGS(PointerChoreographerTest,
-                  DoesNotHidesTouchSpotsOnMirroredDisplaysForInvisibleWindow,
-                  REQUIRES_FLAGS_ENABLED(
-                          ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
-    // Add a touch device and enable show touches.
-    mChoreographer.notifyInputDevicesChanged(
-            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
-    mChoreographer.setShowTouchesEnabled(true);
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+       SkipsPointerScreenshotForPrivacySensitiveWindows) {
+    const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+            GetParam();
+    initializePointerDevice(pointerBuilder, source, onControllerInit, action);
 
-    // Emit touch events to create PointerController
-    mChoreographer.notifyMotion(
-            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
-                    .pointer(FIRST_TOUCH_POINTER)
-                    .deviceId(DEVICE_ID)
-                    .displayId(DISPLAY_ID)
-                    .build());
+    // By default pointer indicators should not be hidden
+    auto pc = assertPointerControllerCreated(controllerType);
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
 
-    // By default touch indicators should not be hidden
-    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
-    pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
-    pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+    // marking a display privacy sensitive should set flag to hide pointer indicators on the
+    // display screenshot
+    gui::WindowInfo windowInfo;
+    windowInfo.displayId = DISPLAY_ID;
+    windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    assertWindowInfosListenerRegistered();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
 
-    // adding secure but hidden window on display should still not set flag to hide pointer
-    // indicators
+    pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+    // un-marking the privacy sensitive display should reset the state
+    windowInfo.inputConfig.clear();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+       DoesNotSkipPointerScreenshotForHiddenPrivacySensitiveWindows) {
+    const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+            GetParam();
+    initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+
+    // By default pointer indicators should not be hidden
+    auto pc = assertPointerControllerCreated(controllerType);
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
     gui::WindowInfo windowInfo;
     windowInfo.displayId = DISPLAY_ID;
     windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
     windowInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_VISIBLE;
-    mChoreographer.onWindowInfosChanged({windowInfo});
-    pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
-    pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    assertWindowInfosListenerRegistered();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+       DoesNotUpdateControllerForUnchangedPrivacySensitiveWindows) {
+    const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+            GetParam();
+    initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+
+    auto pc = assertPointerControllerCreated(controllerType);
+    gui::WindowInfo windowInfo;
+    windowInfo.displayId = DISPLAY_ID;
+    windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    assertWindowInfosListenerRegistered();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    gui::WindowInfo windowInfo2 = windowInfo;
+    windowInfo2.inputConfig.clear();
+    pc->assertSkipScreenshotFlagChanged();
+
+    // controller should not be updated if there are no changes in privacy sensitive windows
+    mRegisteredWindowInfoListener->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                                        {{windowInfo, windowInfo2},
+                                                         {displayInfo},
+                                                         /*vsyncId=*/0,
+                                                         /*timestamp=*/0});
+    pc->assertSkipScreenshotFlagNotChanged();
+}
+
+TEST_F_WITH_FLAGS(
+        PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+                                            hide_pointer_indicators_for_secure_windows))) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+    // Add a first mouse device
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+
+    mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                        .pointer(MOUSE_POINTER)
+                                        .deviceId(DEVICE_ID)
+                                        .displayId(DISPLAY_ID)
+                                        .build());
+
+    gui::WindowInfo windowInfo;
+    windowInfo.displayId = DISPLAY_ID;
+    windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    assertWindowInfosListenerRegistered();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+    // Add a second touch device and controller
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+
+    // Pointer indicators should be hidden for this controller by default
+    auto pc2 = assertPointerControllerCreated(ControllerType::TOUCH);
+    pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+    // un-marking the privacy sensitive display should reset the state
+    windowInfo.inputConfig.clear();
+    mRegisteredWindowInfoListener
+            ->onWindowInfosChanged(/*windowInfosUpdate=*/
+                                   {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+    pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+    pc2->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+    pc2->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
 }
 
 TEST_P(StylusTestFixture, SetsPointerIconForStylus) {
@@ -2073,4 +2294,41 @@
     assertPointerControllerRemoved(pc);
 }
 
+class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
+
+TEST_F_WITH_FLAGS(
+        PointerChoreographerWindowInfoListenerTest,
+        doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
+        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+                                            hide_pointer_indicators_for_secure_windows))) {
+    sp<android::gui::WindowInfosListener> registeredListener;
+    sp<android::gui::WindowInfosListener> localListenerCopy;
+    {
+        testing::NiceMock<MockPointerChoreographerPolicyInterface> mockPolicy;
+        EXPECT_CALL(mockPolicy, createPointerController(ControllerType::MOUSE))
+                .WillOnce(testing::Return(std::make_shared<FakePointerController>()));
+        TestInputListener testListener;
+        std::vector<gui::WindowInfo> injectedInitialWindowInfos;
+        TestPointerChoreographer testChoreographer{testListener, mockPolicy, registeredListener,
+                                                   injectedInitialWindowInfos};
+        testChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+        // Add mouse to create controller and listener
+        testChoreographer.notifyInputDevicesChanged(
+                {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+
+        ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
+        localListenerCopy = registeredListener;
+    }
+    ASSERT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
+
+    gui::WindowInfo windowInfo;
+    windowInfo.displayId = DISPLAY_ID;
+    windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+    gui::DisplayInfo displayInfo;
+    displayInfo.displayId = DISPLAY_ID;
+    localListenerCopy->onWindowInfosChanged(
+            /*windowInfosUpdate=*/{{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index af20a27..8361517 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -81,7 +81,7 @@
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
                                                InputReaderConfiguration::Change(0));
                     RawEvent rawEvent = getFuzzedRawEvent(*fdp);
-                    unused += mapper.process(&rawEvent);
+                    unused += mapper.process(rawEvent);
                 },
                 [&]() -> void {
                     std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 922cbdf..9e02502 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -50,11 +50,10 @@
     FuzzInputReaderContext context(eventHub, fdp);
     InputDevice device = getFuzzedInputDevice(*fdp, &context);
 
-    KeyboardInputMapper& mapper = getMapperForDevice<
-            ThreadSafeFuzzedDataProvider,
-            KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{},
-                                 /*source=*/fdp->ConsumeIntegral<uint32_t>(),
-                                 /*keyboardType=*/fdp->ConsumeIntegral<int32_t>());
+    KeyboardInputMapper& mapper =
+            getMapperForDevice<ThreadSafeFuzzedDataProvider,
+                               KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{},
+                                                    /*source=*/fdp->ConsumeIntegral<uint32_t>());
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
@@ -80,7 +79,7 @@
                 },
                 [&]() -> void {
                     RawEvent rawEvent = getFuzzedRawEvent(*fdp);
-                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(rawEvent);
                 },
                 [&]() -> void {
                     mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 25f2f2e..ff425dd 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -338,9 +338,11 @@
 
     void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
     nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
+    KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; }
 
 private:
     nsecs_t mLastKeyDownTimestamp;
+    std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>();
 };
 
 template <class Fdp>
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index d3f6690..f29577d 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -100,7 +100,7 @@
                 },
                 [&]() -> void {
                     RawEvent rawEvent = getFuzzedRawEvent(*fdp);
-                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(rawEvent);
                 },
                 [&]() -> void {
                     mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index ac2030a..a42d447 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -44,7 +44,7 @@
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
                     RawEvent rawEvent = getFuzzedRawEvent(*fdp);
-                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(rawEvent);
                 },
                 [&]() -> void {
                     mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index 643e8b9..c620032 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -176,7 +176,7 @@
                 },
                 [&]() -> void {
                     RawEvent event = getFuzzedRawEvent(*fdp);
-                    std::list<NotifyArgs> unused = mapper.process(&event);
+                    std::list<NotifyArgs> unused = mapper.process(event);
                 },
         })();
     }
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index afaf0ae..f4b0265 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -84,6 +84,7 @@
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
     ],
 
     static_libs: [
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index 90c2330..7347ac7 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1,3 +1 @@
-arthuri@google.com
 bduddie@google.com
-stange@google.com
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
index 5b499a8..7abfbaa 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -27,4 +27,4 @@
   namespace: "sensors"
   description: "When this flag is enabled, sensor service will only erase dynamic sensor data at the end of the threadLoop to prevent race condition."
   bug: "329020894"
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 8ca796e..1b6c598 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -85,6 +85,7 @@
         "libui",
         "libutils",
         "libSurfaceFlingerProp",
+        "libaconfig_storage_read_api_cc"
     ],
     static_libs: [
         "iinputflinger_aidl_lib_static",
@@ -98,6 +99,7 @@
         "libscheduler",
         "libserviceutils",
         "libshaders",
+        "libsurfaceflingerflags",
         "libtimestats",
         "libtonemap",
     ],
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d420838..191d475 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -306,7 +306,7 @@
     virtual void finishFrame(GpuCompositionResult&&) = 0;
     virtual std::optional<base::unique_fd> composeSurfaces(
             const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
-    virtual void presentFrameAndReleaseLayers() = 0;
+    virtual void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) = 0;
     virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
     virtual bool chooseCompositionStrategy(
             std::optional<android::HWComposer::DeviceRequestedChanges>*) = 0;
@@ -314,6 +314,7 @@
             const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentFrame() = 0;
+    virtual void executeCommands() = 0;
     virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
             bool supportsProtectedContent, ui::Dataspace outputDataspace,
             std::vector<LayerFE*> &outLayerRef) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d87968f..d1eff24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -60,6 +60,7 @@
     void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentFrame() override;
+    void executeCommands() override;
     void setExpensiveRenderingExpected(bool) override;
     void finishFrame(GpuCompositionResult&&) override;
     bool supportsOffloadPresent() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index adcbbb9..9990a74 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -104,7 +104,7 @@
     std::optional<base::unique_fd> composeSurfaces(const Region&,
                                                    std::shared_ptr<renderengine::ExternalTexture>,
                                                    base::unique_fd&) override;
-    void presentFrameAndReleaseLayers() override;
+    void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) override;
     void renderCachedSets(const CompositionRefreshArgs&) override;
     void cacheClientCompositionRequests(uint32_t) override;
     bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
@@ -123,7 +123,8 @@
     virtual std::future<bool> chooseCompositionStrategyAsync(
             std::optional<android::HWComposer::DeviceRequestedChanges>*);
     virtual void resetCompositionStrategy();
-    virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync();
+    virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync(
+            bool flushEvenWhenDisabled);
 
 protected:
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -137,6 +138,7 @@
     void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentFrame() override;
+    void executeCommands() override {}
     virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings(
             const std::shared_ptr<renderengine::ExternalTexture>& buffer) const;
     std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 3f3deae..d5bf2b5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -121,9 +121,10 @@
                                                 base::unique_fd&));
     MOCK_CONST_METHOD0(getSkipColorTransform, bool());
 
-    MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+    MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
     MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+    MOCK_METHOD(void, executeCommands, ());
 
     MOCK_METHOD3(generateClientCompositionRequests,
                  std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index c18be7a..c1617d7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -79,7 +79,7 @@
 }
 
 bool Display::isVirtual() const {
-    return VirtualDisplayId::tryCast(mId).has_value();
+    return mId.isVirtual();
 }
 
 std::optional<DisplayId> Display::getDisplayId() const {
@@ -361,6 +361,15 @@
             static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
 }
 
+void Display::executeCommands() {
+    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    if (mIsDisconnected || !halDisplayIdOpt) {
+        return;
+    }
+
+    getCompositionEngine().getHwComposer().executeCommands(*halDisplayIdOpt);
+}
+
 compositionengine::Output::FrameFences Display::presentFrame() {
     auto fences = impl::Output::presentFrame();
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 84f3f25..5b9a102 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -479,8 +479,9 @@
     devOptRepaintFlash(refreshArgs);
     finishFrame(std::move(result));
     ftl::Future<std::monostate> future;
+    const bool flushEvenWhenDisabled = !refreshArgs.bufferIdsToUncache.empty();
     if (mOffloadPresent) {
-        future = presentFrameAndReleaseLayersAsync();
+        future = presentFrameAndReleaseLayersAsync(flushEvenWhenDisabled);
 
         // Only offload for this frame. The next frame will determine whether it
         // needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing,
@@ -488,7 +489,7 @@
         // we don't want to churn.
         mOffloadPresent = false;
     } else {
-        presentFrameAndReleaseLayers();
+        presentFrameAndReleaseLayers(flushEvenWhenDisabled);
         future = ftl::yield<std::monostate>({});
     }
     renderCachedSets(refreshArgs);
@@ -1100,9 +1101,9 @@
     finishPrepareFrame();
 }
 
-ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() {
+ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
     return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() {
-               presentFrameAndReleaseLayers();
+               presentFrameAndReleaseLayers(flushEvenWhenDisabled);
                return true;
            })))
             .then([](bool) { return std::monostate{}; });
@@ -1177,7 +1178,8 @@
         }
     }
 
-    presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 
     std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
 
@@ -1567,11 +1569,16 @@
     return false;
 }
 
-void Output::presentFrameAndReleaseLayers() {
+void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) {
     ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
     ALOGV(__FUNCTION__);
 
     if (!getState().isEnabled) {
+        if (flushEvenWhenDisabled && FlagManager::getInstance().flush_buffer_slots_to_uncache()) {
+            // Some commands, like clearing buffer slots, should still be executed
+            // even if the display is not enabled.
+            executeCommands();
+        }
         return;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index a95a5c6..39163ea 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -1067,8 +1067,8 @@
 
     EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
     EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
-
-    mDisplay->presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mDisplay->presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 4612117..629d9f2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -63,6 +63,7 @@
                 (override));
     MOCK_METHOD2(presentAndGetReleaseFences,
                  status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
+    MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
     MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
     MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
     MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 0dc3c9f..c34168d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2014,7 +2014,7 @@
         MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
         MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
-        MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+        MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
         MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
         MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine),
@@ -2046,7 +2046,7 @@
     EXPECT_CALL(mOutput, prepareFrame());
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
     EXPECT_CALL(mOutput, finishFrame(_));
-    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
     EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
     mOutput.present(args);
@@ -2067,7 +2067,7 @@
     EXPECT_CALL(mOutput, prepareFrameAsync());
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
     EXPECT_CALL(mOutput, finishFrame(_));
-    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
     EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
     mOutput.present(args);
@@ -2913,7 +2913,7 @@
                      std::optional<base::unique_fd>(const Region&,
                                                     std::shared_ptr<renderengine::ExternalTexture>,
                                                     base::unique_fd&));
-        MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+        MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
         MOCK_METHOD0(prepareFrame, void());
         MOCK_METHOD0(updateProtectedContentState, void());
         MOCK_METHOD2(dequeueRenderBuffer,
@@ -2950,7 +2950,8 @@
     mOutput.mState.isEnabled = false;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+    constexpr bool kFlushEvenWhenDisabled = false;
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
     EXPECT_CALL(mOutput, prepareFrame());
 
     mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2962,7 +2963,8 @@
 
     InSequence seq;
     EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
-    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+    constexpr bool kFlushEvenWhenDisabled = false;
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
     EXPECT_CALL(mOutput, prepareFrame());
 
     mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2978,7 +2980,8 @@
     EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
     EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
-    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+    constexpr bool kFlushEvenWhenDisabled = false;
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
     EXPECT_CALL(mOutput, prepareFrame());
 
     mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2996,7 +2999,7 @@
                      std::optional<base::unique_fd>(const Region&,
                                                     std::shared_ptr<renderengine::ExternalTexture>,
                                                     base::unique_fd&));
-        MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+        MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
         MOCK_METHOD0(updateProtectedContentState, void());
         MOCK_METHOD2(dequeueRenderBuffer,
                      bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
@@ -3139,7 +3142,8 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+        MOCK_METHOD(compositionengine::Output::FrameFences, presentFrame, ());
+        MOCK_METHOD(void, executeCommands, ());
     };
 
     struct Layer {
@@ -3177,9 +3181,67 @@
 };
 
 TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+                      true);
     mOutput.mState.isEnabled = false;
+    EXPECT_CALL(mOutput, executeCommands()).Times(0);
+    EXPECT_CALL(mOutput, presentFrame()).Times(0);
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledExecutesCommandsIfFlush) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+                      true);
+    mOutput.mState.isEnabled = false;
+    EXPECT_CALL(mOutput, executeCommands());
+    EXPECT_CALL(mOutput, presentFrame()).Times(0);
+
+    constexpr bool kFlushEvenWhenDisabled = true;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+                      true);
+    mOutput.mState.isEnabled = true;
+
+    compositionengine::Output::FrameFences frameFences;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // This should only be called for disabled outputs. This test's goal is to verify this line;
+    // the other expectations help satisfy the StrictMocks.
+    EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    constexpr bool kFlushEvenWhenDisabled = true;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands2) {
+    // Same test as ifEnabledDoNotExecuteCommands, but with this variable set to false.
+    constexpr bool kFlushEvenWhenDisabled = false;
+
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+                      true);
+    mOutput.mState.isEnabled = true;
+
+    compositionengine::Output::FrameFences frameFences;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // This should only be called for disabled outputs. This test's goal is to verify this line;
+    // the other expectations help satisfy the StrictMocks.
+    EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3197,7 +3259,8 @@
     EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = true;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
@@ -3241,7 +3304,8 @@
                 EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
             });
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
@@ -3282,7 +3346,8 @@
                 EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
             });
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
@@ -3308,7 +3373,8 @@
     EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
     EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
@@ -3333,7 +3399,8 @@
     EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
 TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
@@ -3381,7 +3448,8 @@
                 EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
             });
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 
     // After the call the list of released layers should have been cleared.
     EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
@@ -3429,7 +3497,8 @@
                 EXPECT_EQ(FenceResult(presentFence), fenceResult);
             });
 
-    mOutput.presentFrameAndReleaseLayers();
+    constexpr bool kFlushEvenWhenDisabled = false;
+    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 
     // After the call the list of released layers should have been cleared.
     EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
@@ -5272,8 +5341,9 @@
     struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock {
         // Set up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD0(presentFrameAndReleaseLayers, void());
-        MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>());
+        MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
+        MOCK_METHOD(ftl::Future<std::monostate>, presentFrameAndReleaseLayersAsync,
+                    (bool flushEvenWhenDisabled));
     };
     OutputPresentFrameAndReleaseLayersAsyncTest() {
         mOutput->setDisplayColorProfileForTest(
@@ -5290,16 +5360,16 @@
 };
 
 TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) {
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0);
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(_)).Times(0);
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(1);
 
     mOutput->present(mRefreshArgs);
 }
 
 TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) {
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(false))
             .WillOnce(Return(ftl::yield<std::monostate>({})));
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0);
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(0);
 
     mOutput->offloadPresentNextFrame();
     mOutput->present(mRefreshArgs);
@@ -5307,9 +5377,10 @@
 
 TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) {
     ::testing::InSequence inseq;
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+    constexpr bool kFlushEvenWhenDisabled = false;
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(kFlushEvenWhenDisabled))
             .WillOnce(Return(ftl::yield<std::monostate>({})));
-    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+    EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled)).Times(1);
 
     mOutput->offloadPresentNextFrame();
     mOutput->present(mRefreshArgs);
@@ -5390,5 +5461,59 @@
     mOutput.updateProtectedContentState();
 }
 
+struct OutputPresentFrameAndReleaseLayersTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test (and functions we can
+        // ignore) to use mock implementations.
+        MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(updateCompositionState,
+                     void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(planComposition, void());
+        MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(beginFrame, void());
+        MOCK_METHOD0(prepareFrame, void());
+        MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
+        MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
+        MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
+        MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+        MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine),
+                    (override));
+        MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
+        MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, (), (override));
+    };
+
+    OutputPresentFrameAndReleaseLayersTest() {
+        EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
+    }
+
+    NiceMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, noBuffersToUncache) {
+    CompositionRefreshArgs args;
+    ASSERT_TRUE(args.bufferIdsToUncache.empty());
+    mOutput.editState().isEnabled = false;
+
+    constexpr bool kFlushEvenWhenDisabled = false;
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+    mOutput.present(args);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, buffersToUncache) {
+    CompositionRefreshArgs args;
+    args.bufferIdsToUncache.push_back(1);
+    mOutput.editState().isEnabled = false;
+
+    constexpr bool kFlushEvenWhenDisabled = true;
+    EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+    mOutput.present(args);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 38cf053..a57e626 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -474,7 +474,6 @@
         features |= RefreshRateOverlay::Features::SetByHwc;
     }
 
-    // TODO(b/296636258) Update to use the render rate range in VRR mode.
     const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
     mRefreshRateOverlay = RefreshRateOverlay::create(fpsRange, features);
     if (mRefreshRateOverlay) {
@@ -489,6 +488,9 @@
     ATRACE_CALL();
     if (mRefreshRateOverlay) {
         if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) {
+            if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) {
+                refreshRate = renderFps;
+            }
             mRefreshRateOverlay->changeRefreshRate(refreshRate, renderFps);
         } else {
             mRefreshRateOverlay->changeRenderRate(renderFps);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b339bc6..a21559f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -89,7 +89,7 @@
         return mCompositionDisplay;
     }
 
-    bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); }
+    bool isVirtual() const { return getId().isVirtual(); }
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3cfb9ca..3d285a8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -600,6 +600,13 @@
     return NO_ERROR;
 }
 
+status_t HWComposer::executeCommands(HalDisplayId displayId) {
+    auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+    auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId()));
+    RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
 status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index bc32cda..9368b7b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,6 +160,8 @@
             HalDisplayId,
             std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) = 0;
 
+    virtual status_t executeCommands(HalDisplayId) = 0;
+
     // set power mode
     virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
 
@@ -361,6 +363,8 @@
             HalDisplayId,
             std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) override;
 
+    status_t executeCommands(HalDisplayId) override;
+
     // set power mode
     status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
 
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 0dcbb3c..39a6b77 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -52,8 +52,12 @@
     mChildren = hierarchy.mChildren;
 }
 
-void LayerHierarchy::traverse(const Visitor& visitor,
-                              LayerHierarchy::TraversalPath& traversalPath) const {
+void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& traversalPath,
+                              uint32_t depth) const {
+    LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
+                                    "Cycle detected in LayerHierarchy::traverse. See "
+                                    "traverse_stack_overflow_transactions.winscope");
+
     if (mLayer) {
         bool breakTraversal = !visitor(*this, traversalPath);
         if (breakTraversal) {
@@ -66,7 +70,7 @@
     for (auto& [child, childVariant] : mChildren) {
         ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
                                                          childVariant);
-        child->traverse(visitor, traversalPath);
+        child->traverse(visitor, traversalPath, depth + 1);
     }
 }
 
@@ -256,27 +260,36 @@
     hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
 }
 
-void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
-    if (root->mLayer) {
-        attachToRelativeParent(root);
-    }
-    for (auto& [child, childVariant] : root->mChildren) {
-        if (childVariant == LayerHierarchy::Variant::Detached ||
-            childVariant == LayerHierarchy::Variant::Attached) {
-            attachHierarchyToRelativeParent(child);
+std::vector<LayerHierarchy*> LayerHierarchyBuilder::getDescendants(LayerHierarchy* root) {
+    std::vector<LayerHierarchy*> hierarchies;
+    hierarchies.push_back(root);
+    std::vector<LayerHierarchy*> descendants;
+    for (size_t i = 0; i < hierarchies.size(); i++) {
+        LayerHierarchy* hierarchy = hierarchies[i];
+        if (hierarchy->mLayer) {
+            descendants.push_back(hierarchy);
         }
+        for (auto& [child, childVariant] : hierarchy->mChildren) {
+            if (childVariant == LayerHierarchy::Variant::Detached ||
+                childVariant == LayerHierarchy::Variant::Attached) {
+                hierarchies.push_back(child);
+            }
+        }
+    }
+    return descendants;
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+    std::vector<LayerHierarchy*> hierarchiesToAttach = getDescendants(root);
+    for (LayerHierarchy* hierarchy : hierarchiesToAttach) {
+        attachToRelativeParent(hierarchy);
     }
 }
 
 void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
-    if (root->mLayer) {
-        detachFromRelativeParent(root);
-    }
-    for (auto& [child, childVariant] : root->mChildren) {
-        if (childVariant == LayerHierarchy::Variant::Detached ||
-            childVariant == LayerHierarchy::Variant::Attached) {
-            detachHierarchyFromRelativeParent(child);
-        }
+    std::vector<LayerHierarchy*> hierarchiesToDetach = getDescendants(root);
+    for (LayerHierarchy* hierarchy : hierarchiesToDetach) {
+        detachFromRelativeParent(hierarchy);
     }
 }
 
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index f62e758..d023f9e 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -147,7 +147,7 @@
         if (mLayer) {
             root.id = mLayer->id;
         }
-        traverse(visitor, root);
+        traverse(visitor, root, /*depth=*/0);
     }
 
     // Traverse the hierarchy in z-order, skipping children that have relative parents.
@@ -190,7 +190,8 @@
     void sortChildrenByZOrder();
     void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
     void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
-    void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+    void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent,
+                  uint32_t depth = 0) const;
     void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
               bool isLastChild, bool includeMirroredHierarchy) const;
 
@@ -217,6 +218,7 @@
     void detachFromParent(LayerHierarchy*);
     void attachToRelativeParent(LayerHierarchy*);
     void detachFromRelativeParent(LayerHierarchy*);
+    std::vector<LayerHierarchy*> getDescendants(LayerHierarchy*);
     void attachHierarchyToRelativeParent(LayerHierarchy*);
     void detachHierarchyFromRelativeParent(LayerHierarchy*);
     void init(const std::vector<std::unique_ptr<RequestedLayerState>>&);
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 4b0618e..dd5e8bd 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -152,6 +152,10 @@
             if (swapErase(linkedLayer->mirrorIds, layer.id)) {
                 linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
             }
+            if (linkedLayer->layerIdToMirror == layer.id) {
+                linkedLayer->layerIdToMirror = UNASSIGNED_LAYER_ID;
+                linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
+            }
             if (linkedLayer->touchCropId == layer.id) {
                 linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
             }
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 6d4b0b5..ca53a0d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -256,6 +256,9 @@
 }
 
 void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+    if (snapshot.isVisible != visible) {
+        snapshot.changes |= RequestedLayerState::Changes::Visibility;
+    }
     snapshot.isVisible = visible;
 
     // TODO(b/238781169) we are ignoring this compat for now, since we will have
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index b40f332..9527a99 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -200,19 +200,13 @@
     BufferCache::const_iterator it =
             mBufferCache.find({refreshRate.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
-        // HWC minFps is not known by the framework in order
-        // to consider lower rates we set minFps to 0.
-        const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
-        // Clamp to the range. The current refreshRate may be outside of this range if the display
-        // has changed its set of supported refresh rates.
-        const int displayIntFps = std::clamp(refreshRate.getIntValue(), minFps, maxFps);
+        // Clamp to supported refresh rate range: the current refresh rate may be outside of this
+        // range if the display has changed its set of supported refresh rates.
+        const int refreshIntFps = std::clamp(refreshRate.getIntValue(), 0, maxFps);
         const int renderIntFps = renderFps.getIntValue();
-
-        // Ensure non-zero range to avoid division by zero.
-        const float fpsScale =
-                static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
+        const float fpsScale = static_cast<float>(refreshIntFps) / maxFps;
 
         constexpr SkColor kMinFpsColor = SK_ColorRED;
         constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -228,9 +222,9 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures);
+        auto buffers = draw(refreshIntFps, renderIntFps, color, transformHint, mFeatures);
         it = mBufferCache
-                     .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+                     .try_emplace({refreshIntFps, renderIntFps, transformHint}, std::move(buffers))
                      .first;
     }
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 93ec36e..b2896f0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -65,7 +65,7 @@
 
     using Buffers = std::vector<sp<GraphicBuffer>>;
 
-    static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags,
+    static Buffers draw(int refreshRate, int renderFps, SkColor, ui::Transform::RotationFlags,
                         ftl::Flags<Features>);
     static void drawNumber(int number, int left, SkColor, SkCanvas&);
 
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 2b4e234..5add290 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -315,39 +315,15 @@
         return true;
     };
 
-    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
-    if (mFlinger.mLayerLifecycleManagerEnabled) {
-        auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
-                            bool& outStopTraversal) -> bool {
-            const Rect bounds =
-                    frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
-                                                          snapshot.transparentRegionHint);
-            const ui::Transform transform = snapshot.geomLayerTransform;
-            return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
-                                 outStopTraversal);
-        };
-        getLayerSnapshots =
-                mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
-                                                         filterFn);
-    } else {
-        auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
-            bool stopLayerFound = false;
-            auto filterVisitor = [&](Layer* layer) {
-                // We don't want to capture any layers beyond the stop layer
-                if (stopLayerFound) return;
-
-                if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
-                                   Rect(layer->getBounds()), layer->getTransform(),
-                                   stopLayerFound)) {
-                    return;
-                }
-                visitor(layer);
-            };
-            mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {},
-                                                filterVisitor);
-        };
-        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
-    }
+    auto filterFn = [&](const frontend::LayerSnapshot& snapshot, bool& outStopTraversal) -> bool {
+        const Rect bounds = frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+                                                                  snapshot.transparentRegionHint);
+        const ui::Transform transform = snapshot.geomLayerTransform;
+        return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+                             outStopTraversal);
+    };
+    auto getLayerSnapshotsFn =
+            mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, filterFn);
 
     std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
     if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -372,17 +348,30 @@
     constexpr bool kGrayscale = false;
     constexpr bool kIsProtected = false;
 
-    if (const auto fenceResult =
-                mFlinger.captureScreenshot(SurfaceFlinger::RenderAreaBuilderVariant(
-                                                   std::in_place_type<DisplayRenderAreaBuilder>,
-                                                   sampledBounds, sampledBounds.getSize(),
-                                                   ui::Dataspace::V0_SRGB,
-                                                   kHintForSeamlessTransition,
-                                                   true /* captureSecureLayers */, displayWeak),
-                                           getLayerSnapshots, buffer, kRegionSampling, kGrayscale,
-                                           kIsProtected, nullptr)
+    SurfaceFlinger::RenderAreaBuilderVariant
+            renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
+                              sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
+                              kHintForSeamlessTransition, true /* captureSecureLayers */,
+                              displayWeak);
+
+    FenceResult fenceResult;
+    if (FlagManager::getInstance().single_hop_screenshot() &&
+        FlagManager::getInstance().ce_fence_promise()) {
+        std::vector<sp<LayerFE>> layerFEs;
+        auto displayState =
+                mFlinger.getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder,
+                                                                   getLayerSnapshotsFn, layerFEs);
+        fenceResult =
+                mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
+                                           kIsProtected, nullptr, displayState, layerFEs)
                         .get();
-        fenceResult.ok()) {
+    } else {
+        fenceResult =
+                mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer,
+                                                 kRegionSampling, kGrayscale, kIsProtected, nullptr)
+                        .get();
+    }
+    if (fenceResult.ok()) {
         fenceResult.value()->waitForever(LOG_TAG);
     }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 85ce713..dd3c4b0 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -737,7 +737,9 @@
         return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
     };
 
-    Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns());
+    Fps displayFps = !FlagManager::getInstance().vrr_bugfix_24q4() && mRenderRateOpt
+            ? *mRenderRateOpt
+            : Fps::fromPeriodNsecs(mIdealPeriod.ns());
     const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
     const auto now = TimePoint::now();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c0faf16..694f147 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -40,6 +40,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <common/FlagManager.h>
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/CompositionRefreshArgs.h>
@@ -813,8 +814,24 @@
                 .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
     } else {
         const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK;
+// TODO: b/341728634 - Clean up conditional compilation.
+// Note: this guard in particular must check e.g.
+// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE directly (instead of calling e.g.
+// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE)) because that macro is undefined
+// in the libsurfaceflingerflags_test variant of com_android_graphics_surfaceflinger_flags.h, which
+// is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
+        COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
         const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
                 renderengine::RenderEngine::canSupport(kVulkan);
+#else
+        const bool useGraphite = false;
+        if (FlagManager::getInstance().graphite_renderengine()) {
+            ALOGE("RenderEngine's Graphite Skia backend was requested with the "
+                  "debug.renderengine.graphite system property, but it is not compiled in this "
+                  "build! Falling back to Ganesh backend selection logic.");
+        }
+#endif
         const bool useVulkan = useGraphite ||
                 (FlagManager::getInstance().vulkan_renderengine() &&
                  renderengine::RenderEngine::canSupport(kVulkan));
@@ -7837,14 +7854,13 @@
 
 namespace {
 
-ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display,
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace,
+                                const compositionengine::impl::OutputCompositionState& state,
                                 bool capturingHdrLayers, bool hintForSeamlessTransition) {
-    if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) {
+    if (requestedDataspace != ui::Dataspace::UNKNOWN) {
         return requestedDataspace;
     }
 
-    const auto& state = display->getCompositionDisplay()->getState();
-
     const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
 
     // TODO: Enable once HDR screenshots are ready.
@@ -7924,23 +7940,14 @@
         }
     }
 
-    GetLayerSnapshotsFunction getLayerSnapshots;
-    if (mLayerLifecycleManagerEnabled) {
-        getLayerSnapshots =
-                getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
-    } else {
-        auto traverseLayers = [this, args, excludeLayerIds,
-                               layerStack](const LayerVector::Visitor& visitor) {
-            traverseLayersInLayerStack(layerStack, args.uid, std::move(excludeLayerIds), visitor);
-        };
-        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
-    }
+    GetLayerSnapshotsFunction getLayerSnapshotsFn =
+            getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
 
     captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
                                                  args.sourceCrop, reqSize, args.dataspace,
                                                  args.hintForSeamlessTransition,
                                                  args.captureSecureLayers, displayWeak),
-                        getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+                        getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
                         args.grayscale, captureListener);
 }
 
@@ -7977,16 +7984,9 @@
         return;
     }
 
-    GetLayerSnapshotsFunction getLayerSnapshots;
-    if (mLayerLifecycleManagerEnabled) {
-        getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
-                                                            /*snapshotFilterFn=*/nullptr);
-    } else {
-        auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
-            traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {}, visitor);
-        };
-        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
-    }
+    GetLayerSnapshotsFunction getLayerSnapshotsFn =
+            getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+                                            /*snapshotFilterFn=*/nullptr);
 
     if (captureListener == nullptr) {
         ALOGE("capture screen must provide a capture listener callback");
@@ -8001,7 +8001,7 @@
                                                  Rect(), size, args.dataspace,
                                                  args.hintForSeamlessTransition,
                                                  false /* captureSecureLayers */, displayWeak),
-                        getLayerSnapshots, size, args.pixelFormat, kAllowProtected, kGrayscale,
+                        getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale,
                         captureListener);
 }
 
@@ -8083,42 +8083,16 @@
         return;
     }
 
-    GetLayerSnapshotsFunction getLayerSnapshots;
-    if (mLayerLifecycleManagerEnabled) {
-        std::optional<FloatRect> parentCrop = std::nullopt;
-        if (args.childrenOnly) {
-            parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
-                                        : crop.toFloatRect();
-        }
-
-        getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid,
-                                                            std::move(excludeLayerIds),
-                                                            args.childrenOnly, parentCrop);
-    } else {
-        auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
-            parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-                if (!layer->isVisible()) {
-                    return;
-                } else if (args.childrenOnly && layer == parent.get()) {
-                    return;
-                } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
-                    return;
-                }
-
-                auto p = sp<Layer>::fromExisting(layer);
-                while (p != nullptr) {
-                    if (excludeLayerIds.count(p->sequence) != 0) {
-                        return;
-                    }
-                    p = p->getParent();
-                }
-
-                visitor(layer);
-            });
-        };
-        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    std::optional<FloatRect> parentCrop = std::nullopt;
+    if (args.childrenOnly) {
+        parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
+                                    : crop.toFloatRect();
     }
 
+    GetLayerSnapshotsFunction getLayerSnapshotsFn =
+            getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds),
+                                            args.childrenOnly, parentCrop);
+
     if (captureListener == nullptr) {
         ALOGD("capture screen must provide a capture listener callback");
         invokeScreenCaptureError(BAD_VALUE, captureListener);
@@ -8129,7 +8103,7 @@
                                                  reqSize, dataspace, args.captureSecureLayers,
                                                  args.hintForSeamlessTransition, parent,
                                                  args.childrenOnly),
-                        getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+                        getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
                         args.grayscale, captureListener);
 }
 
@@ -8144,12 +8118,15 @@
     owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack);
 }
 
-bool SurfaceFlinger::layersHasProtectedLayer(
-        const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
+// 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
+// application to avoid being screenshot or drawn via unsecure display.
+bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const {
     bool protectedLayerFound = false;
-    for (auto& [_, layerFe] : layers) {
+    for (auto& layerFE : layers) {
         protectedLayerFound |=
-                (layerFe->mSnapshot->isVisible && layerFe->mSnapshot->hasProtectedContent);
+                (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent);
         if (protectedLayerFound) {
             break;
         }
@@ -8157,8 +8134,28 @@
     return protectedLayerFound;
 }
 
+// Getting layer snapshots and display should take place on main thread.
+// Accessing display requires mStateLock, and contention for this lock
+// is reduced when grabbed from the main thread, thus also reducing
+// risk of deadlocks.
+std::optional<SurfaceFlinger::OutputCompositionState>
+SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread(
+        RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
+        std::vector<sp<LayerFE>>& layerFEs) {
+    return mScheduler
+            ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) {
+                auto layers = getLayerSnapshotsFn();
+                for (auto& [layer, layerFE] : layers) {
+                    attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+                }
+                layerFEs = extractLayerFEs(layers);
+                return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
+            })
+            .get();
+}
+
 void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
-                                         GetLayerSnapshotsFunction getLayerSnapshots,
+                                         GetLayerSnapshotsFunction getLayerSnapshotsFn,
                                          ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
                                          bool allowProtected, bool grayscale,
                                          const sp<IScreenCaptureListener>& captureListener) {
@@ -8172,74 +8169,185 @@
         return;
     }
 
-    // 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
-    // application to avoid being screenshot or drawn via unsecure display.
-    const bool supportsProtected = getRenderEngine().supportsProtectedContent();
-    bool hasProtectedLayer = false;
-    if (allowProtected && supportsProtected) {
-        auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get();
-        hasProtectedLayer = layersHasProtectedLayer(layers);
-    }
-    const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
-    const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
-            GRALLOC_USAGE_HW_TEXTURE |
-            (isProtected ? GRALLOC_USAGE_PROTECTED
-                         : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
-    sp<GraphicBuffer> buffer =
-            getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
-                                             static_cast<android_pixel_format>(reqPixelFormat),
-                                             1 /* layerCount */, usage, "screenshot");
+    if (FlagManager::getInstance().single_hop_screenshot() &&
+        FlagManager::getInstance().ce_fence_promise()) {
+        std::vector<sp<LayerFE>> layerFEs;
+        auto displayState =
+                getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn,
+                                                          layerFEs);
 
-    const status_t bufferStatus = buffer->initCheck();
-    if (bufferStatus != OK) {
-        // Animations may end up being really janky, but don't crash here.
-        // Otherwise an irreponsible process may cause an SF crash by allocating
-        // too much.
-        ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
-        invokeScreenCaptureError(bufferStatus, captureListener);
-        return;
+        const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+        bool hasProtectedLayer = false;
+        if (allowProtected && supportsProtected) {
+            hasProtectedLayer = layersHasProtectedLayer(layerFEs);
+        }
+        const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+        const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+                GRALLOC_USAGE_HW_TEXTURE |
+                (isProtected ? GRALLOC_USAGE_PROTECTED
+                             : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+        sp<GraphicBuffer> buffer =
+                getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+                                                 static_cast<android_pixel_format>(reqPixelFormat),
+                                                 1 /* layerCount */, usage, "screenshot");
+
+        const status_t bufferStatus = buffer->initCheck();
+        if (bufferStatus != OK) {
+            // Animations may end up being really janky, but don't crash here.
+            // Otherwise an irreponsible process may cause an SF crash by allocating
+            // too much.
+            ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+            invokeScreenCaptureError(bufferStatus, captureListener);
+            return;
+        }
+        const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+                renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             WRITEABLE);
+        auto futureFence =
+                captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
+                                  isProtected, captureListener, displayState, layerFEs);
+        futureFence.get();
+
+    } else {
+        const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+        bool hasProtectedLayer = false;
+        if (allowProtected && supportsProtected) {
+            auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get();
+            hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers));
+        }
+        const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+        const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+                GRALLOC_USAGE_HW_TEXTURE |
+                (isProtected ? GRALLOC_USAGE_PROTECTED
+                             : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+        sp<GraphicBuffer> buffer =
+                getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+                                                 static_cast<android_pixel_format>(reqPixelFormat),
+                                                 1 /* layerCount */, usage, "screenshot");
+
+        const status_t bufferStatus = buffer->initCheck();
+        if (bufferStatus != OK) {
+            // Animations may end up being really janky, but don't crash here.
+            // Otherwise an irreponsible process may cause an SF crash by allocating
+            // too much.
+            ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+            invokeScreenCaptureError(bufferStatus, captureListener);
+            return;
+        }
+        const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+                renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             WRITEABLE);
+        auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
+                                                   false /* regionSampling */, grayscale,
+                                                   isProtected, captureListener);
+        futureFence.get();
     }
-    const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
-            renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
-                                                 renderengine::impl::ExternalTexture::Usage::
-                                                         WRITEABLE);
-    auto futureFence =
-            captureScreenshot(renderAreaBuilder, getLayerSnapshots, texture,
-                              false /* regionSampling */, grayscale, isProtected, captureListener);
-    futureFence.get();
 }
 
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
-        RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshots,
-        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
-    ATRACE_CALL();
-
-    auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
-                                    kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
-        // LayerSnapshots must be obtained from the main thread.
-        auto layers = getLayerSnapshots();
-
+std::optional<SurfaceFlinger::OutputCompositionState>
+SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& renderAreaBuilder) {
+    sp<const DisplayDevice> display = nullptr;
+    {
+        Mutex::Autolock lock(mStateLock);
         if (auto* layerRenderAreaBuilder =
                     std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) {
             // LayerSnapshotBuilder should only be accessed from the main thread.
-            frontend::LayerSnapshot* snapshot =
+            const frontend::LayerSnapshot* snapshot =
                     mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence());
             if (!snapshot) {
                 ALOGW("Couldn't find layer snapshot for %d",
                       layerRenderAreaBuilder->layer->getSequence());
             } else {
                 layerRenderAreaBuilder->setLayerSnapshot(*snapshot);
+                display = findDisplay(
+                        [layerStack = snapshot->outputFilter.layerStack](const auto& display) {
+                            return display.getLayerStack() == layerStack;
+                        });
             }
+        } else if (auto* displayRenderAreaBuilder =
+                           std::get_if<DisplayRenderAreaBuilder>(&renderAreaBuilder)) {
+            display = displayRenderAreaBuilder->displayWeak.promote();
         }
 
+        if (display == nullptr) {
+            display = getDefaultDisplayDeviceLocked();
+        }
+
+        if (display != nullptr) {
+            return std::optional{display->getCompositionDisplay()->getState()};
+        }
+    }
+    return std::nullopt;
+}
+
+std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs(
+        const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
+    std::vector<sp<LayerFE>> layerFEs;
+    layerFEs.reserve(layers.size());
+    for (const auto& [_, layerFE] : layers) {
+        layerFEs.push_back(layerFE);
+    }
+    return layerFEs;
+}
+
+ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
+        const RenderAreaBuilderVariant& renderAreaBuilder,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+        bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+        std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
+    ATRACE_CALL();
+
+    ScreenCaptureResults captureResults;
+    std::unique_ptr<const RenderArea> renderArea =
+            std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); },
+                       renderAreaBuilder);
+
+    if (!renderArea) {
+        ALOGW("Skipping screen capture because of invalid render area.");
+        if (captureListener) {
+            captureResults.fenceResult = base::unexpected(NO_MEMORY);
+            captureListener->onScreenCaptureCompleted(captureResults);
+        }
+        return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+    }
+
+    // Empty vector needed to pass into renderScreenImpl for legacy path
+    std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
+    ftl::SharedFuture<FenceResult> renderFuture =
+            renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
+                             captureResults, displayState, layers, layerFEs);
+
+    if (captureListener) {
+        // Defer blocking on renderFuture back to the Binder thread.
+        return ftl::Future(std::move(renderFuture))
+                .then([captureListener, captureResults = std::move(captureResults)](
+                              FenceResult fenceResult) mutable -> FenceResult {
+                    captureResults.fenceResult = std::move(fenceResult);
+                    captureListener->onScreenCaptureCompleted(captureResults);
+                    return base::unexpected(NO_ERROR);
+                })
+                .share();
+    }
+    return renderFuture;
+}
+
+ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
+        RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+        bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
+    ATRACE_CALL();
+
+    auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
+                                    kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+        auto layers = getLayerSnapshotsFn();
         if (FlagManager::getInstance().ce_fence_promise()) {
             for (auto& [layer, layerFE] : layers) {
                 attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
             }
         }
+        auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
 
         ScreenCaptureResults captureResults;
         std::unique_ptr<const RenderArea> renderArea =
@@ -8255,9 +8363,10 @@
             return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
         }
 
+        auto layerFEs = extractLayerFEs(layers);
         ftl::SharedFuture<FenceResult> renderFuture =
                 renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
-                                 isProtected, captureResults, layers);
+                                 isProtected, captureResults, displayState, layers, layerFEs);
 
         if (captureListener) {
             // Defer blocking on renderFuture back to the Binder thread.
@@ -8287,22 +8396,14 @@
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        std::unique_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
-        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) {
-    auto layers = getLayerSnapshots();
-    return renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
-                            captureResults, layers);
-}
-
-ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
         std::unique_ptr<const RenderArea> renderArea,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
-        std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) {
+        std::optional<OutputCompositionState>& displayState,
+        std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
     ATRACE_CALL();
 
-    for (auto& [_, layerFE] : layers) {
+    for (auto& layerFE : layerFEs) {
         frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
         captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
         captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
@@ -8325,78 +8426,54 @@
     const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() &&
             !renderArea->getHintForSeamlessTransition();
 
-    {
-        Mutex::Autolock lock(mStateLock);
-        const DisplayDevice* display = nullptr;
-        if (parent) {
-            const frontend::LayerSnapshot* snapshot =
-                    mLayerSnapshotBuilder.getSnapshot(parent->sequence);
-            if (snapshot) {
-                display = findDisplay([layerStack = snapshot->outputFilter.layerStack](
-                                              const auto& display) {
-                              return display.getLayerStack() == layerStack;
-                          }).get();
-            }
-        }
+    if (displayState) {
+        const auto& state = displayState.value();
+        captureResults.capturedDataspace =
+                pickBestDataspace(requestedDataspace, state, captureResults.capturedHdrLayers,
+                                  renderArea->getHintForSeamlessTransition());
+        sdrWhitePointNits = state.sdrWhitePointNits;
 
-        if (display == nullptr) {
-            display = renderArea->getDisplayDevice().get();
-        }
-
-        if (display == nullptr) {
-            display = getDefaultDisplayDeviceLocked().get();
-        }
-
-        if (display != nullptr) {
-            const auto& state = display->getCompositionDisplay()->getState();
-            captureResults.capturedDataspace =
-                    pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
-                                      renderArea->getHintForSeamlessTransition());
-            sdrWhitePointNits = state.sdrWhitePointNits;
-
-            if (!captureResults.capturedHdrLayers) {
-                displayBrightnessNits = sdrWhitePointNits;
-            } else {
-                displayBrightnessNits = state.displayBrightnessNits;
-
-                if (!enableLocalTonemapping) {
-                    // Only clamp the display brightness if this is not a seamless transition.
-                    // Otherwise for seamless transitions it's important to match the current
-                    // display state as the buffer will be shown under these same conditions, and we
-                    // want to avoid any flickers
-                    if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
-                        // Restrict the amount of HDR "headroom" in the screenshot to avoid
-                        // over-dimming the SDR portion. 2.0 chosen by experimentation
-                        constexpr float kMaxScreenshotHeadroom = 2.0f;
-                        displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
-                                                         displayBrightnessNits);
-                    }
+        if (!captureResults.capturedHdrLayers) {
+            displayBrightnessNits = sdrWhitePointNits;
+        } else {
+            displayBrightnessNits = state.displayBrightnessNits;
+            if (!enableLocalTonemapping) {
+                // Only clamp the display brightness if this is not a seamless transition.
+                // Otherwise for seamless transitions it's important to match the current
+                // display state as the buffer will be shown under these same conditions, and we
+                // want to avoid any flickers
+                if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
+                    // Restrict the amount of HDR "headroom" in the screenshot to avoid
+                    // over-dimming the SDR portion. 2.0 chosen by experimentation
+                    constexpr float kMaxScreenshotHeadroom = 2.0f;
+                    displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
+                                                     displayBrightnessNits);
                 }
             }
+        }
 
-            // Screenshots leaving the device should be colorimetric
-            if (requestedDataspace == ui::Dataspace::UNKNOWN &&
-                renderArea->getHintForSeamlessTransition()) {
-                renderIntent = state.renderIntent;
-            }
+        // Screenshots leaving the device should be colorimetric
+        if (requestedDataspace == ui::Dataspace::UNKNOWN &&
+            renderArea->getHintForSeamlessTransition()) {
+            renderIntent = state.renderIntent;
         }
     }
 
     captureResults.buffer = capturedBuffer->getBuffer();
 
     ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
-    if (!layers.empty()) {
-        const sp<LayerFE>& layerFE = layers.back().second;
+    if (!layerFEs.empty()) {
+        const sp<LayerFE>& layerFE = layerFEs.back();
         layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
     }
 
-    auto copyLayerFEs = [&layers]() {
-        std::vector<sp<compositionengine::LayerFE>> layerFEs;
-        layerFEs.reserve(layers.size());
-        for (const auto& [_, layerFE] : layers) {
-            layerFEs.push_back(layerFE);
+    auto copyLayerFEs = [&layerFEs]() {
+        std::vector<sp<compositionengine::LayerFE>> ceLayerFEs;
+        ceLayerFEs.reserve(layerFEs.size());
+        for (const auto& layerFE : layerFEs) {
+            ceLayerFEs.push_back(layerFE);
         }
-        return layerFEs;
+        return ceLayerFEs;
     };
 
     auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
@@ -8465,8 +8542,16 @@
     //
     // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
     // to CompositionEngine::present.
-    auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
-                                                     : ftl::yield(present()).share();
+    ftl::SharedFuture<FenceResult> presentFuture;
+    if (FlagManager::getInstance().single_hop_screenshot() &&
+        FlagManager::getInstance().ce_fence_promise()) {
+        presentFuture = mRenderEngine->isThreaded()
+                ? ftl::yield(present()).share()
+                : mScheduler->schedule(std::move(present)).share();
+    } else {
+        presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
+                                                    : ftl::yield(present()).share();
+    }
 
     if (!FlagManager::getInstance().ce_fence_promise()) {
         for (auto& [layer, layerFE] : layers) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8a39016..1230717 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -892,32 +892,46 @@
     void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
 
     // Checks if a protected layer exists in a list of layers.
-    bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
+    bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const;
+
+    using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+    std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread(
+            RenderAreaBuilderVariant& renderAreaBuilder,
+            GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs);
 
     void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
                              ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
                              bool grayscale, const sp<IScreenCaptureListener>&);
 
+    std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
+            RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
+
+    // Legacy layer raw pointer is not safe to access outside the main thread.
+    // Creates a new vector consisting only of LayerFEs, which can be safely
+    // accessed outside the main thread.
+    std::vector<sp<LayerFE>> extractLayerFEs(
+            const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
+
     ftl::SharedFuture<FenceResult> captureScreenshot(
+            const RenderAreaBuilderVariant& renderAreaBuilder,
+            const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+            bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+            std::optional<OutputCompositionState>& displayState,
+            std::vector<sp<LayerFE>>& layerFEs);
+
+    ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
             RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
 
-    // Overloaded version of renderScreenImpl that is used when layer snapshots have
-    // not yet been captured, and thus cannot yet be passed in as a parameter.
-    // Needed for TestableSurfaceFlinger.
-    ftl::SharedFuture<FenceResult> renderScreenImpl(
-            std::unique_ptr<const RenderArea>, GetLayerSnapshotsFunction,
-            const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
-            bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock)
-            REQUIRES(kMainThreadContext);
-
     ftl::SharedFuture<FenceResult> renderScreenImpl(
             std::unique_ptr<const RenderArea>,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, bool isProtected, ScreenCaptureResults&,
-            std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) EXCLUDES(mStateLock)
-            REQUIRES(kMainThreadContext);
+            std::optional<OutputCompositionState>& displayState,
+            std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+            std::vector<sp<LayerFE>>& layerFEs);
 
     // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
     // matching ownerUid
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 6b971a7..c3594bc 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -17,6 +17,7 @@
     shared_libs: [
         "libSurfaceFlingerProp",
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
     ],
     static_libs: [
         "librenderengine_includes",
@@ -56,6 +57,7 @@
     name: "libsurfaceflinger_common_deps",
     shared_libs: [
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
     ],
     static_libs: [
         "libsurfaceflinger_common",
@@ -69,6 +71,7 @@
     name: "libsurfaceflinger_common_test_deps",
     shared_libs: [
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
     ],
     static_libs: [
         "libsurfaceflinger_common_test",
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 57b170f..4216771 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -148,6 +148,9 @@
     DUMP_READ_ONLY_FLAG(commit_not_composited);
     DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
     DUMP_READ_ONLY_FLAG(override_trusted_overlay);
+    DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
+    DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine);
+    DUMP_READ_ONLY_FLAG(single_hop_screenshot);
 
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
@@ -246,6 +249,9 @@
 FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
 FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
 FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
+FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
+FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
+FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 9517ff7..22118ab 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -87,6 +87,9 @@
     bool commit_not_composited() const;
     bool local_tonemap_screenshots() const;
     bool override_trusted_overlay() const;
+    bool flush_buffer_slots_to_uncache() const;
+    bool force_compile_graphite_renderengine() const;
+    bool single_hop_screenshot() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index dea74d0..56bca7f 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -161,7 +161,7 @@
 flag {
   name: "graphite_renderengine"
   namespace: "core_graphics"
-  description: "Use Skia's Graphite Vulkan backend in RenderEngine."
+  description: "Compile AND enable Skia's Graphite Vulkan backend in RenderEngine. See also: force_compile_graphite_renderengine."
   bug: "293371537"
   is_fixed_read_only: true
 }
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 02d8819..f4d4ee9 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -55,6 +55,25 @@
 } # detached_mirror
 
 flag {
+  name: "flush_buffer_slots_to_uncache"
+  namespace: "core_graphics"
+  description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers."
+  bug: "330806421"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+} # flush_buffer_slots_to_uncache
+
+flag {
+  name: "force_compile_graphite_renderengine"
+  namespace: "core_graphics"
+  description: "Compile Skia's Graphite Vulkan backend in RenderEngine, but do NOT enable it, unless graphite_renderengine is also set. It can also be enabled with the debug.renderengine.graphite system property for testing. In contrast, the graphite_renderengine flag both compiles AND enables Graphite in RenderEngine."
+  bug: "293371537"
+  is_fixed_read_only: true
+} # force_compile_graphite_renderengine
+
+flag {
   name: "frame_rate_category_mrr"
   namespace: "core_graphics"
   description: "Enable to use frame rate category and newer frame rate votes such as GTE in SurfaceFlinger scheduler, to guard dVRR changes from MRR devices"
@@ -84,6 +103,17 @@
     is_fixed_read_only: true
 } # local_tonemap_screenshots
 
+flag {
+  name: "single_hop_screenshot"
+  namespace: "window_surfaces"
+  description: "Only access SF main thread once during a screenshot"
+  bug: "285553970"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+ } # single_hop_screenshot
+
  flag {
   name: "override_trusted_overlay"
   namespace: "window_surfaces"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 0ddddbd..08973de 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -205,7 +205,8 @@
                                                    CaptureArgs::UNSET_UID, {}, visitor);
     };
 
-    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function
+    auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -215,7 +216,7 @@
                                                                       HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                                                       usage);
 
-    auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
+    auto future = mFlinger.renderScreenImpl(mDisplay, std::move(renderArea), getLayerSnapshotsFn,
                                             mCaptureScreenBuffer, regionSampling);
     ASSERT_TRUE(future.valid());
     const auto fenceResult = future.get();
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 2b333f4..b79bdb4 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -777,4 +777,28 @@
     EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
 }
 
+// (b/343901186)
+TEST_F(LayerHierarchyTest, cleanUpDanglingMirrorLayer) {
+    LayerHierarchyBuilder hierarchyBuilder;
+    hierarchyBuilder.update(mLifecycleManager);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 2, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // destroy layer handle
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index bd0accd..dedb292 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -213,6 +213,17 @@
     UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
 }
 
+TEST_F(LayerSnapshotTest, offscreenLayerSnapshotIsInvisible) {
+    EXPECT_EQ(getSnapshot(111)->isVisible, true);
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+    EXPECT_EQ(getSnapshot(111)->isVisible, false);
+    EXPECT_TRUE(getSnapshot(111)->changes.test(RequestedLayerState::Changes::Visibility));
+}
+
 // relative tests
 TEST_F(LayerSnapshotTest, RelativeParentCanHideChild) {
     reparentRelativeLayer(13, 11);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 265f804..8c72a7d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -485,23 +485,28 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto renderScreenImpl(std::unique_ptr<const RenderArea> renderArea,
-                          SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
+    auto renderScreenImpl(const sp<DisplayDevice> display,
+                          std::unique_ptr<const RenderArea> renderArea,
+                          SurfaceFlinger::GetLayerSnapshotsFunction getLayerSnapshotsFn,
                           const std::shared_ptr<renderengine::ExternalTexture>& buffer,
                           bool regionSampling) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        ftl::FakeGuard guard(kMainThreadContext);
+
         ScreenCaptureResults captureResults;
-        return FTL_FAKE_GUARD(kMainThreadContext,
-                              mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers,
-                                                         buffer, regionSampling,
-                                                         false /* grayscale */,
-                                                         false /* isProtected */, captureResults));
+        auto displayState = std::optional{display->getCompositionDisplay()->getState()};
+        auto layers = getLayerSnapshotsFn();
+        auto layerFEs = mFlinger->extractLayerFEs(layers);
+
+        return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling,
+                                          false /* grayscale */, false /* isProtected */,
+                                          captureResults, displayState, layers, layerFEs);
     }
 
     auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
                                     std::unordered_set<uint32_t> excludeLayerIds,
                                     const LayerVector::Visitor& visitor) {
-        return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid,
-                                                                    excludeLayerIds, visitor);
+        return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor);
     }
 
     auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index eafba0a..5109ea6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -535,6 +535,28 @@
     }
 }
 
+TEST_F(VSyncPredictorTest, isVSyncInPhaseWithRenderRate) {
+    SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true);
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + mPeriod), Eq(mNow + 2 * mPeriod));
+
+    const auto renderRateFps = Fps::fromPeriodNsecs(mPeriod * 2);
+    tracker.setRenderRate(renderRateFps, /*applyImmediately*/ true);
+
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow, renderRateFps));
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + mPeriod, renderRateFps));
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 2 * mPeriod, renderRateFps));
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 3 * mPeriod, renderRateFps));
+}
+
 TEST_F(VSyncPredictorTest, isVSyncInPhaseForDivisors) {
     auto last = mNow;
     for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 74d3d9d..9e67725 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1174,7 +1174,8 @@
                             pSurfaceFormat);
 
                     if (surfaceCompressionProps &&
-                        driver.GetPhysicalDeviceImageFormatProperties2KHR) {
+                        (driver.GetPhysicalDeviceImageFormatProperties2KHR ||
+                         driver.GetPhysicalDeviceImageFormatProperties2)) {
                         VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
                         imageFormatInfo.sType =
                             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
@@ -1205,7 +1206,7 @@
                         imageFormatProps.pNext = &compressionProps;
 
                         VkResult compressionRes =
-                            driver.GetPhysicalDeviceImageFormatProperties2KHR(
+                            GetPhysicalDeviceImageFormatProperties2(
                                 physicalDevice, &imageFormatInfo,
                                 &imageFormatProps);
                         if (compressionRes == VK_SUCCESS) {