Merge "Move external MultiTouch test into InputMapperUnitTest" into main
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h
index 90427bd..f3f5148 100644
--- a/include/input/DisplayTopologyGraph.h
+++ b/include/input/DisplayTopologyGraph.h
@@ -43,7 +43,7 @@
 struct DisplayTopologyAdjacentDisplay {
     ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
     DisplayTopologyPosition position;
-    float offsetPx;
+    float offsetDp;
 };
 
 /**
@@ -52,6 +52,7 @@
 struct DisplayTopologyGraph {
     ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID;
     std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph;
+    std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
 };
 
 } // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index 2cabd56..e84023e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -92,11 +92,23 @@
             static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE),
 
     /**
-     * This event was generated or modified by accessibility service.
+     * This event was injected from some AccessibilityService, which may be either an
+     * Accessibility Tool OR a service using that API for purposes other than assisting users
+     * with disabilities.
      */
     AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
             static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT),
 
+    /**
+     * This event was injected from an AccessibilityService with the
+     * AccessibilityServiceInfo#isAccessibilityTool property set to true. These services (known as
+     * "Accessibility Tools") are used to assist users with disabilities, so events from these
+     * services should be able to reach all Views including Views which set
+     * View#isAccessibilityDataSensitive to true.
+     */
+    AMOTION_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL =
+            static_cast<int32_t>(android::os::MotionEventFlag::INJECTED_FROM_ACCESSIBILITY_TOOL),
+
     AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS =
             static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS),
 
@@ -347,6 +359,9 @@
     POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
             android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
 
+    POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL =
+            android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL,
+
     /* These flags are set by the input dispatcher. */
 
     // Indicates that the input event was injected.
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 0a22588..bc7ae37 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -38,6 +38,7 @@
 #endif
 
 #include "BuildFlags.h"
+#include "Constants.h"
 #include "OS.h"
 #include "RpcState.h"
 
@@ -70,8 +71,6 @@
 constexpr bool kEnableRecording = false;
 #endif
 
-// Log any reply transactions for which the data exceeds this size
-#define LOG_REPLIES_OVER_SIZE (300 * 1024)
 // ---------------------------------------------------------------------------
 
 IBinder::IBinder()
@@ -412,7 +411,7 @@
     // In case this is being transacted on in the same process.
     if (reply != nullptr) {
         reply->setDataPosition(0);
-        if (reply->dataSize() > LOG_REPLIES_OVER_SIZE) {
+        if (reply->dataSize() > binder::kLogTransactionsOverBytes) {
             ALOGW("Large reply transaction of %zu bytes, interface descriptor %s, code %d",
                   reply->dataSize(), String8(getInterfaceDescriptor()).c_str(), code);
         }
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 444f061..c13e0f9 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -28,6 +28,7 @@
 #include <stdio.h>
 
 #include "BuildFlags.h"
+#include "Constants.h"
 #include "file.h"
 
 //#undef ALOGV
@@ -63,9 +64,6 @@
 
 static constexpr uint32_t kBinderProxyCountWarnInterval = 5000;
 
-// Log any transactions for which the data exceeds this size
-#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024)
-
 enum {
     LIMIT_REACHED_MASK = 0x80000000,        // A flag denoting that the limit has been reached
     WARNING_REACHED_MASK = 0x40000000,      // A flag denoting that the warning has been reached
@@ -403,9 +401,11 @@
 
             status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
         }
-        if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
+
+        if (data.dataSize() > binder::kLogTransactionsOverBytes) {
             RpcMutexUniqueLock _l(mLock);
-            ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d",
+            ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d was "
+                  "sent",
                   data.dataSize(), String8(mDescriptorCache).c_str(), code);
         }
 
diff --git a/libs/binder/Constants.h b/libs/binder/Constants.h
new file mode 100644
index 0000000..b75493c
--- /dev/null
+++ b/libs/binder/Constants.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 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
+
+namespace android::binder {
+
+/**
+ * See also BINDER_VM_SIZE. In kernel binder, the sum of all transactions must be allocated in this
+ * space. Large transactions are very error prone. In general, we should work to reduce this limit.
+ * The same limit is used in RPC binder for consistency.
+ */
+constexpr size_t kLogTransactionsOverBytes = 300 * 1024;
+
+/**
+ * See b/392575419 - this limit is chosen for a specific usecase, because RPC binder does not have
+ * support for shared memory in the Android Baklava timeframe. This was 100 KB during and before
+ * Android V.
+ *
+ * Keeping this low helps preserve overall system performance. Transactions of this size are far too
+ * expensive to make multiple copies over binder or sockets, and they should be avoided if at all
+ * possible and transition to shared memory.
+ */
+constexpr size_t kRpcTransactionLimitBytes = 600 * 1024;
+
+} // namespace android::binder
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index fe6e1a3..03d974d 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -23,6 +23,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/RpcServer.h>
 
+#include "Constants.h"
 #include "Debug.h"
 #include "RpcWireFormat.h"
 #include "Utils.h"
@@ -337,6 +338,8 @@
 }
 
 RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+    if (size == 0) return;
+
     // The maximum size for regular binder is 1MB for all concurrent
     // transactions. A very small proportion of transactions are even
     // larger than a page, but we need to avoid allocating too much
@@ -348,11 +351,11 @@
     // transaction (in some cases, additional fixed size amounts are added),
     // though for rough consistency, we should avoid cases where this data type
     // is used for multiple dynamic allocations for a single transaction.
-    constexpr size_t kMaxTransactionAllocation = 100 * 1000;
-    if (size == 0) return;
-    if (size > kMaxTransactionAllocation) {
-        ALOGW("Transaction requested too much data allocation %zu", size);
+    if (size > binder::kRpcTransactionLimitBytes) {
+        ALOGE("Transaction requested too much data allocation: %zu bytes, failing.", size);
         return;
+    } else if (size > binder::kLogTransactionsOverBytes) {
+        ALOGW("Transaction too large: inefficient and in danger of breaking: %zu bytes.", size);
     }
     mData.reset(new (std::nothrow) uint8_t[size]);
 }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 77b80fe..0026f21 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -116,7 +116,7 @@
 pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
 pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
 #[cfg(not(trusty))]
-pub use persistable_bundle::PersistableBundle;
+pub use persistable_bundle::{PersistableBundle, ValueType};
 pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
 #[cfg(not(any(trusty, android_ndk)))]
 pub use service::{
diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs
index e1d64ad..8639c0d 100644
--- a/libs/binder/rust/src/persistable_bundle.rs
+++ b/libs/binder/rust/src/persistable_bundle.rs
@@ -22,11 +22,17 @@
 };
 use binder_ndk_sys::{
     APersistableBundle, APersistableBundle_delete, APersistableBundle_dup,
-    APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanVector,
-    APersistableBundle_getDouble, APersistableBundle_getDoubleVector, APersistableBundle_getInt,
-    APersistableBundle_getIntVector, APersistableBundle_getLong, APersistableBundle_getLongVector,
-    APersistableBundle_getPersistableBundle, APersistableBundle_getString,
-    APersistableBundle_getStringVector, APersistableBundle_isEqual, APersistableBundle_new,
+    APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanKeys,
+    APersistableBundle_getBooleanVector, APersistableBundle_getBooleanVectorKeys,
+    APersistableBundle_getDouble, APersistableBundle_getDoubleKeys,
+    APersistableBundle_getDoubleVector, APersistableBundle_getDoubleVectorKeys,
+    APersistableBundle_getInt, APersistableBundle_getIntKeys, APersistableBundle_getIntVector,
+    APersistableBundle_getIntVectorKeys, APersistableBundle_getLong,
+    APersistableBundle_getLongKeys, APersistableBundle_getLongVector,
+    APersistableBundle_getLongVectorKeys, APersistableBundle_getPersistableBundle,
+    APersistableBundle_getPersistableBundleKeys, APersistableBundle_getString,
+    APersistableBundle_getStringKeys, APersistableBundle_getStringVector,
+    APersistableBundle_getStringVectorKeys, APersistableBundle_isEqual, APersistableBundle_new,
     APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
     APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
     APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
@@ -588,6 +594,171 @@
             Ok(None)
         }
     }
+
+    /// Calls the appropriate `APersistableBundle_get*Keys` function for the given `value_type`,
+    /// with our `string_allocator` and a null context pointer.
+    ///
+    /// # Safety
+    ///
+    /// `out_keys` must either be null or point to a buffer of at least `buffer_size_bytes` bytes,
+    /// properly aligned for `T`, and not otherwise accessed for the duration of the call.
+    unsafe fn get_keys_raw(
+        &self,
+        value_type: ValueType,
+        out_keys: *mut *mut c_char,
+        buffer_size_bytes: i32,
+    ) -> i32 {
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. Our caller guarantees an appropriate value for
+        // `out_keys` and `buffer_size_bytes`.
+        unsafe {
+            match value_type {
+                ValueType::Boolean => APersistableBundle_getBooleanKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::Integer => APersistableBundle_getIntKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::Long => APersistableBundle_getLongKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::Double => APersistableBundle_getDoubleKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::String => APersistableBundle_getStringKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::BooleanVector => APersistableBundle_getBooleanVectorKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::IntegerVector => APersistableBundle_getIntVectorKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::LongVector => APersistableBundle_getLongVectorKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::DoubleVector => APersistableBundle_getDoubleVectorKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::StringVector => APersistableBundle_getStringVectorKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+                ValueType::PersistableBundle => APersistableBundle_getPersistableBundleKeys(
+                    self.0.as_ptr(),
+                    out_keys,
+                    buffer_size_bytes,
+                    Some(string_allocator),
+                    null_mut(),
+                ),
+            }
+        }
+    }
+
+    /// Gets all the keys associated with values of the given type.
+    pub fn keys_for_type(&self, value_type: ValueType) -> Vec<String> {
+        // SAFETY: A null pointer is allowed for the buffer.
+        match unsafe { self.get_keys_raw(value_type, null_mut(), 0) } {
+            APERSISTABLEBUNDLE_ALLOCATOR_FAILED => {
+                panic!("APersistableBundle_get*Keys failed to allocate string");
+            }
+            required_buffer_size => {
+                let required_buffer_size_usize = usize::try_from(required_buffer_size)
+                    .expect("APersistableBundle_get*Keys returned invalid size");
+                assert_eq!(required_buffer_size_usize % size_of::<*mut c_char>(), 0);
+                let mut keys =
+                    vec![null_mut(); required_buffer_size_usize / size_of::<*mut c_char>()];
+                // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
+                // the lifetime of the `PersistableBundle`. The keys buffer pointer is valid as it
+                // comes from the Vec we just allocated.
+                if unsafe { self.get_keys_raw(value_type, keys.as_mut_ptr(), required_buffer_size) }
+                    == APERSISTABLEBUNDLE_ALLOCATOR_FAILED
+                {
+                    panic!("APersistableBundle_get*Keys failed to allocate string");
+                }
+                keys.into_iter()
+                    .map(|key| {
+                        // SAFETY: The pointer was returned from `string_allocator`, which used
+                        // `Box::into_raw`, and `APersistableBundle_getStringVector` should have
+                        // written valid bytes to it including a NUL terminator in the last
+                        // position.
+                        let string_length = unsafe { CStr::from_ptr(key) }.count_bytes();
+                        let raw_slice = slice_from_raw_parts_mut(key.cast(), string_length + 1);
+                        // SAFETY: The pointer was returned from `string_allocator`, which used
+                        // `Box::into_raw`, and we've got the appropriate size back by checking the
+                        // length of the string.
+                        let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) };
+                        let c_string = CString::from_vec_with_nul(boxed_slice.into())
+                            .expect("APersistableBundle_get*Keys returned string missing NUL byte");
+                        c_string
+                            .into_string()
+                            .expect("APersistableBundle_get*Keys returned invalid UTF-8")
+                    })
+                    .collect()
+            }
+        }
+    }
+
+    /// Returns an iterator over all keys in the bundle, along with the type of their associated
+    /// value.
+    pub fn keys(&self) -> impl Iterator<Item = (String, ValueType)> + use<'_> {
+        [
+            ValueType::Boolean,
+            ValueType::Integer,
+            ValueType::Long,
+            ValueType::Double,
+            ValueType::String,
+            ValueType::BooleanVector,
+            ValueType::IntegerVector,
+            ValueType::LongVector,
+            ValueType::DoubleVector,
+            ValueType::StringVector,
+            ValueType::PersistableBundle,
+        ]
+        .iter()
+        .flat_map(|value_type| {
+            self.keys_for_type(*value_type).into_iter().map(|key| (key, *value_type))
+        })
+    }
 }
 
 /// Wrapper around `APersistableBundle_getStringVector` to pass `string_allocator` and a null
@@ -709,6 +880,33 @@
 impl_deserialize_for_unstructured_parcelable!(PersistableBundle);
 impl_serialize_for_unstructured_parcelable!(PersistableBundle);
 
+/// The types which may be stored as values in a [`PersistableBundle`].
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ValueType {
+    /// A `bool`.
+    Boolean,
+    /// An `i32`.
+    Integer,
+    /// An `i64`.
+    Long,
+    /// An `f64`.
+    Double,
+    /// A string.
+    String,
+    /// A vector of `bool`s.
+    BooleanVector,
+    /// A vector of `i32`s.
+    IntegerVector,
+    /// A vector of `i64`s.
+    LongVector,
+    /// A vector of `f64`s.
+    DoubleVector,
+    /// A vector of strings.
+    StringVector,
+    /// A nested `PersistableBundle`.
+    PersistableBundle,
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -846,4 +1044,33 @@
 
         assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle)));
     }
+
+    #[test]
+    fn get_keys() {
+        let mut bundle = PersistableBundle::new();
+
+        assert_eq!(bundle.keys_for_type(ValueType::Boolean), Vec::<String>::new());
+        assert_eq!(bundle.keys_for_type(ValueType::Integer), Vec::<String>::new());
+        assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
+
+        assert_eq!(bundle.insert_bool("bool1", false), Ok(()));
+        assert_eq!(bundle.insert_bool("bool2", true), Ok(()));
+        assert_eq!(bundle.insert_int("int", 42), Ok(()));
+
+        assert_eq!(
+            bundle.keys_for_type(ValueType::Boolean),
+            vec!["bool1".to_string(), "bool2".to_string()]
+        );
+        assert_eq!(bundle.keys_for_type(ValueType::Integer), vec!["int".to_string()]);
+        assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new());
+
+        assert_eq!(
+            bundle.keys().collect::<Vec<_>>(),
+            vec![
+                ("bool1".to_string(), ValueType::Boolean),
+                ("bool2".to_string(), ValueType::Boolean),
+                ("int".to_string(), ValueType::Integer),
+            ]
+        );
+    }
 }
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 1164767..dcd6461 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -34,6 +34,8 @@
     void holdBinder(@nullable IBinder binder);
     @nullable IBinder getHeldBinder();
 
+    byte[] repeatBytes(in byte[] bytes);
+
     // Idea is client creates its own instance of IBinderRpcTest and calls this,
     // and the server calls 'binder' with (calls - 1) passing itself as 'binder',
     // going back and forth until calls = 0
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 9f656ec..e88e3f3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -711,6 +711,35 @@
     proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
 }
 
+// TODO(b/392717039): can we move this to universal tests?
+TEST_P(BinderRpc, SendTooLargeVector) {
+    if (GetParam().singleThreaded) {
+        GTEST_SKIP() << "Requires multi-threaded server to test one of the sessions crashing.";
+    }
+
+    auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+    // need a working transaction
+    EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+    // see libbinder internal Constants.h
+    const size_t kTooLargeSize = 650 * 1024;
+    const std::vector<uint8_t> kTestValue(kTooLargeSize / sizeof(uint8_t), 42);
+
+    // TODO(b/392717039): Telling a server to allocate too much data currently causes the session to
+    // close since RpcServer treats any transaction error as a failure. We likely want to change
+    // this behavior to be a soft failure, since it isn't hard to keep track of this state.
+    sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
+    std::vector<uint8_t> result;
+    status_t res = rootIface2->repeatBytes(kTestValue, &result).transactionError();
+
+    // TODO(b/392717039): consistent error results always
+    EXPECT_TRUE(res == -ECONNRESET || res == DEAD_OBJECT) << statusToString(res);
+
+    // died, so remove it for checks in destructor of proc
+    proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
+}
+
 TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) {
     if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index dc22647..6e00246 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -348,6 +348,10 @@
         *out = binder;
         return Status::ok();
     }
+    Status repeatBytes(const std::vector<uint8_t>& bytes, std::vector<uint8_t>* out) override {
+        *out = bytes;
+        return Status::ok();
+    }
     static sp<IBinder> mHeldBinder;
     Status holdBinder(const sp<IBinder>& binder) override {
         mHeldBinder = binder;
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 4b9dcf8..d227e6e 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -209,6 +209,18 @@
     EXPECT_EQ(0, MyBinderRpcSession::gNum);
 }
 
+TEST_P(BinderRpc, SendLargeVector) {
+    auto proc = createRpcTestSocketServerProcess({});
+
+    // see libbinder internal Constants.h
+    const size_t kLargeSize = 550 * 1024;
+    const std::vector<uint8_t> kTestValue(kLargeSize / sizeof(uint8_t), 42);
+
+    std::vector<uint8_t> result;
+    EXPECT_OK(proc.rootIface->repeatBytes(kTestValue, &result));
+    EXPECT_EQ(result, kTestValue);
+}
+
 TEST_P(BinderRpc, RepeatTheirBinder) {
     auto proc = createRpcTestSocketServerProcess({});
 
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
index 6e20b8a..da0f2ed 100644
--- a/libs/binder/trusty/binderRpcTest/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -1,6 +1,6 @@
 {
     "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
     "app_name": "binderRpcTest",
-    "min_heap": 262144,
+    "min_heap": 4194304,
     "min_stack": 20480
 }
diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json
index d2a1fc0..55ff49c 100644
--- a/libs/binder/trusty/binderRpcTest/service/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/service/manifest.json
@@ -1,7 +1,7 @@
 {
     "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94",
     "app_name": "binderRpcTestService",
-    "min_heap": 65536,
+    "min_heap": 4194304,
     "min_stack": 20480,
     "mgmt_flags": {
         "restart_on_exit": true,
diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
index 22cba44..caf3117 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
@@ -82,6 +82,9 @@
     fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> {
         todo!()
     }
+    fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> {
+        todo!()
+    }
     fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> {
         todo!()
     }
diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
index c4a758a..6f454be 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
@@ -96,6 +96,9 @@
             None => Err(Status::from(StatusCode::BAD_VALUE)),
         }
     }
+    fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> {
+        todo!()
+    }
     fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> {
         *HOLD_BINDER.lock().unwrap() = binder.cloned();
         Ok(())
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 6ce3fba..4b87dab 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -43,8 +43,9 @@
     const int INVALID_INPUT_DEVICE_ID = -2;
 
     /**
-     * The input event was injected from accessibility. Used in policyFlags for input event
-     * injection.
+     * The input event was injected from some AccessibilityService, which may be either an
+     * Accessibility Tool OR a service using that API for purposes other than assisting users with
+     * disabilities. Used in policyFlags for input event injection.
      */
     const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
 
@@ -55,18 +56,33 @@
     const int POLICY_FLAG_KEY_GESTURE_TRIGGERED = 0x40000;
 
     /**
+     * The input event was injected from an AccessibilityService with the
+     * AccessibilityServiceInfo#isAccessibilityTool property set to true. These services (known as
+     * "Accessibility Tools") are used to assist users with disabilities, so events from these
+     * services should be able to reach all Views including Views which set
+     * View#isAccessibilityDataSensitive to true. Used in policyFlags for input event injection.
+     */
+    const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = 0x80000;
+
+    /**
      * Common input event flag used for both motion and key events for a gesture or pointer being
      * canceled.
      */
     const int INPUT_EVENT_FLAG_CANCELED = 0x20;
 
     /**
-     * Common input event flag used for both motion and key events, indicating that the event
-     * was generated or modified by accessibility service.
+     * Input event flag used for both motion and key events.
+     * See POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY for more information.
      */
     const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
 
     /**
+     * Input event flag used for motion events.
+     * See POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL for more information.
+     */
+    const int INPUT_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = 0x1000;
+
+    /**
      * Common input event flag used for both motion and key events, indicating that the system has
      * detected this event may be inconsistent with the current event sequence or gesture, such as
      * when a pointer move event is sent but the pointer is not down.
diff --git a/libs/input/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl
index 2093b06..6c9ccfb 100644
--- a/libs/input/android/os/MotionEventFlag.aidl
+++ b/libs/input/android/os/MotionEventFlag.aidl
@@ -118,13 +118,24 @@
     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.
+     * The input event was injected from some AccessibilityService, which may be either an
+     * Accessibility Tool OR a service using that API for purposes other than assisting users with
+     * disabilities. 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.
      */
     IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
 
     /**
+     * The input event was injected from an AccessibilityService with the
+     * AccessibilityServiceInfo#isAccessibilityTool property set to true. These services (known as
+     * "Accessibility Tools") are used to assist users with disabilities, so events from these
+     * services should be able to reach all Views including Views which set
+     * View#isAccessibilityDataSensitive to true. Only used by MotionEvent flags.
+     */
+    INJECTED_FROM_ACCESSIBILITY_TOOL =
+        IInputConstants.INPUT_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL,
+
+    /**
      * Private flag that indicates when the system has detected that this motion event
      * may be inconsistent with respect to the sequence of previously delivered motion events,
      * such as when a pointer move event is sent but the pointer is not down.
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index f17575d..72a6fdf 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -79,13 +79,6 @@
 }
 
 flag {
-  name: "enable_input_filter_rust_impl"
-  namespace: "input"
-  description: "Enable input filter rust implementation"
-  bug: "294546335"
-}
-
-flag {
   name: "override_key_behavior_permission_apis"
   is_exported: true
   namespace: "input"
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 90f509d..6956a84 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -219,6 +219,9 @@
                 MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32;
         /// FLAG_IS_ACCESSIBILITY_EVENT
         const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32;
+        /// FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL
+        const INJECTED_FROM_ACCESSIBILITY_TOOL =
+                MotionEventFlag::INJECTED_FROM_ACCESSIBILITY_TOOL.0 as u32;
         /// FLAG_TAINTED
         const TAINTED = MotionEventFlag::TAINTED.0 as u32;
         /// FLAG_TARGET_ACCESSIBILITY_FOCUS
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index b1d7760..bddd2a7 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -39,8 +39,7 @@
         | MotionAction::Up => {
             if pointer_count != 1 {
                 return Err(format!(
-                    "Invalid {} event: there are {} pointers in the event",
-                    action, pointer_count
+                    "Invalid {action} event: there are {pointer_count} pointers in the event",
                 ));
             }
         }
@@ -48,15 +47,14 @@
         MotionAction::Cancel => {
             if !flags.contains(MotionFlags::CANCELED) {
                 return Err(format!(
-                    "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}",
-                    flags
+                    "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {flags:#?}",
                 ));
             }
         }
 
         MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
             if action_index >= pointer_count {
-                return Err(format!("Got {}, but event has {} pointer(s)", action, pointer_count));
+                return Err(format!("Got {action}, but event has {pointer_count} pointer(s)"));
             }
         }
 
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
index 4a6e849..800bf3c 100644
--- a/libs/tracing_perfetto/include/tracing_sdk.h
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -27,6 +27,10 @@
 #include "perfetto/public/te_macros.h"
 #include "perfetto/public/track_event.h"
 
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/tracing_session.h"
+
 /**
  * The objects declared here are intended to be managed by Java.
  * This means the Java Garbage Collector is responsible for freeing the
@@ -452,6 +456,22 @@
   std::vector<PerfettoTeHlProtoField*> fields_;
 };
 
+class Session {
+ public:
+  Session(bool is_backend_in_process, void* buf, size_t len);
+  ~Session();
+  Session(const Session&) = delete;
+  Session& operator=(const Session&) = delete;
+
+  bool FlushBlocking(uint32_t timeout_ms);
+  void StopBlocking();
+  std::vector<uint8_t> ReadBlocking();
+
+  static void delete_session(Session* session);
+
+  struct PerfettoTracingSessionImpl* session_ = nullptr;
+};
+
 /**
  * @brief Activates a trigger.
  * @param name The name of the trigger.
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
index 0dab517..79fb704 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -21,44 +21,12 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_static {
-    name: "libtracing_perfetto_test_utils",
-    export_include_dirs: [
-        "include",
-    ],
-    static_libs: [
-        "libflagtest",
-        "libgmock",
-        "perfetto_trace_protos",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-enum-compare",
-        "-Wno-unused-function",
-    ],
-
-    srcs: [
-        "utils.cpp",
-    ],
-
-    shared_libs: [
-        "libperfetto_c",
-        "liblog",
-        "libprotobuf-cpp-lite",
-    ],
-    export_shared_lib_headers: [
-        "libperfetto_c",
-    ],
-}
-
 cc_test {
     name: "libtracing_perfetto_tests",
     static_libs: [
         "libflagtest",
         "libgmock",
         "perfetto_trace_protos",
-        "libtracing_perfetto_test_utils",
     ],
     cflags: [
         "-Wall",
@@ -68,12 +36,15 @@
         "android.os.flags-aconfig-cc-host",
         "libbase",
         "libperfetto_c",
-        "liblog",
         "libprotobuf-cpp-lite",
         "libtracing_perfetto",
     ],
     srcs: [
         "tracing_perfetto_test.cpp",
+        "utils.cpp",
+    ],
+    local_include_dirs: [
+        "include",
     ],
     test_suites: ["device-tests"],
 }
diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp
index 02e8d10..c97e900 100644
--- a/libs/tracing_perfetto/tracing_sdk.cpp
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -254,6 +254,47 @@
   return &field_;
 }
 
+Session::Session(bool is_backend_in_process, void* buf, size_t len) {
+  session_ = PerfettoTracingSessionCreate(is_backend_in_process
+                                              ? PERFETTO_BACKEND_IN_PROCESS
+                                              : PERFETTO_BACKEND_SYSTEM);
+
+  PerfettoTracingSessionSetup(session_, buf, len);
+
+  PerfettoTracingSessionStartBlocking(session_);
+}
+
+Session::~Session() {
+  PerfettoTracingSessionStopBlocking(session_);
+  PerfettoTracingSessionDestroy(session_);
+}
+
+bool Session::FlushBlocking(uint32_t timeout_ms) {
+  return PerfettoTracingSessionFlushBlocking(session_, timeout_ms);
+}
+
+void Session::StopBlocking() {
+  PerfettoTracingSessionStopBlocking(session_);
+}
+
+std::vector<uint8_t> Session::ReadBlocking() {
+  std::vector<uint8_t> data;
+  PerfettoTracingSessionReadTraceBlocking(
+      session_,
+      [](struct PerfettoTracingSessionImpl*, const void* trace_data,
+         size_t size, bool, void* user_arg) {
+        auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg);
+        auto* src = static_cast<const uint8_t*>(trace_data);
+        dst.insert(dst.end(), src, src + size);
+      },
+      &data);
+  return data;
+}
+
+void Session::delete_session(Session* ptr) {
+  delete ptr;
+}
+
 void activate_trigger(const char* name, uint32_t ttl_ms) {
   const char* names[] = {name, nullptr};
   PerfettoProducerActivateTriggers(names, ttl_ms);
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index b155122..7d62ed9 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -41,8 +41,6 @@
 const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
         sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
 
-const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
-
 int32_t exceptionCodeFromStatusT(status_t status) {
     switch (status) {
         case OK:
@@ -134,12 +132,10 @@
     mTracingStages.emplace_back(
             std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
 
-    if (ENABLE_INPUT_FILTER_RUST) {
-        mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
-                                                     inputFilterPolicy);
-        mTracingStages.emplace_back(
-                std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
-    }
+    mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
+                                                 inputFilterPolicy);
+    mTracingStages.emplace_back(
+            std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
 
     if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
         mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
@@ -250,10 +246,8 @@
         mCollector->dump(dump);
         dump += '\n';
     }
-    if (ENABLE_INPUT_FILTER_RUST) {
-        mInputFilter->dump(dump);
-        dump += '\n';
-    }
+    mInputFilter->dump(dump);
+    dump += '\n';
     mDispatcher->dump(dump);
     dump += '\n';
 }
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 5bf6ebb..9f91285 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -1017,9 +1017,9 @@
                         sourceBoundary == DisplayTopologyPosition::BOTTOM
                 ? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
                 : (destinationViewport->logicalBottom - destinationViewport->logicalTop);
-        if (cursorOffset >= adjacentDisplay.offsetPx &&
-            cursorOffset <= adjacentDisplay.offsetPx + edgeSize) {
-            return std::make_pair(destinationViewport, adjacentDisplay.offsetPx);
+        if (cursorOffset >= adjacentDisplay.offsetDp &&
+            cursorOffset <= adjacentDisplay.offsetDp + edgeSize) {
+            return std::make_pair(destinationViewport, adjacentDisplay.offsetDp);
         }
     }
     return std::nullopt;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0412dc5..098019f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -918,6 +918,21 @@
     return inputTarget;
 }
 
+std::string dumpWindowForTouchOcclusion(const WindowInfo& info, bool isTouchedWindow) {
+    return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
+                                "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+                                "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
+                                "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
+                        isTouchedWindow ? "[TOUCHED] " : "", info.packageName.c_str(),
+                        info.ownerUid.toString().c_str(), info.id,
+                        toString(info.touchOcclusionMode).c_str(), info.alpha, info.frame.left,
+                        info.frame.top, info.frame.right, info.frame.bottom,
+                        dumpRegion(info.touchableRegion).c_str(), info.name.c_str(),
+                        info.inputConfig.string().c_str(), toString(info.token != nullptr),
+                        info.applicationInfo.name.c_str(),
+                        binderToString(info.applicationInfo.token).c_str());
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -933,13 +948,12 @@
         mLastDropReason(DropReason::NOT_DROPPED),
         mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
         mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
+        mConnectionManager(mLooper),
         mNextUnblockedEvent(nullptr),
         mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
-        mMaximumObscuringOpacityForTouch(1.0f),
-        mConnectionManager(mLooper),
         mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
         mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID),
@@ -2485,9 +2499,9 @@
             if (isSplit) {
                 targetFlags |= InputTarget::Flags::SPLIT;
             }
-            if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
+            if (mWindowInfos.isWindowObscuredAtPoint(windowHandle, x, y)) {
                 targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
-            } else if (isWindowObscuredLocked(windowHandle)) {
+            } else if (mWindowInfos.isWindowObscured(windowHandle)) {
                 targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
             }
 
@@ -2518,7 +2532,8 @@
                 if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) &&
                     windowHandle->getInfo()->inputConfig.test(
                             gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
-                    sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
+                    sp<WindowInfoHandle> wallpaper =
+                            mWindowInfos.findWallpaperWindowBelow(windowHandle);
                     if (wallpaper != nullptr) {
                         ftl::Flags<InputTarget::Flags> wallpaperFlags =
                                 InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2630,9 +2645,9 @@
                 if (isSplit) {
                     targetFlags |= InputTarget::Flags::SPLIT;
                 }
-                if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
+                if (mWindowInfos.isWindowObscuredAtPoint(newTouchedWindowHandle, x, y)) {
                     targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
-                } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+                } else if (mWindowInfos.isWindowObscured(newTouchedWindowHandle)) {
                     targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
                 }
 
@@ -3085,12 +3100,12 @@
  *
  * If neither of those is true, then it means the touch can be allowed.
  */
-InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+InputDispatcher::DispatcherWindowInfo::TouchOcclusionInfo
+InputDispatcher::DispatcherWindowInfo::computeTouchOcclusionInfo(
         const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
     const WindowInfo* windowInfo = windowHandle->getInfo();
     ui::LogicalDisplayId displayId = windowInfo->displayId;
-    const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            mWindowInfos.getWindowHandlesForDisplay(displayId);
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
     TouchOcclusionInfo info;
     info.hasBlockingOcclusion = false;
     info.obscuringOpacity = 0;
@@ -3102,12 +3117,11 @@
         }
         const WindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) &&
-            windowOccludesTouchAt(*otherInfo, displayId, x, y,
-                                  mWindowInfos.getDisplayTransform(displayId)) &&
+            windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId)) &&
             !haveSameApplicationToken(windowInfo, otherInfo)) {
             if (DEBUG_TOUCH_OCCLUSION) {
                 info.debugInfo.push_back(
-                        dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false));
+                        dumpWindowForTouchOcclusion(*otherInfo, /*isTouchedWindow=*/false));
             }
             // canBeObscuredBy() has returned true above, which means this window is untrusted, so
             // we perform the checks below to see if the touch can be propagated or not based on the
@@ -3135,28 +3149,14 @@
         }
     }
     if (DEBUG_TOUCH_OCCLUSION) {
-        info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true));
+        info.debugInfo.push_back(
+                dumpWindowForTouchOcclusion(*windowInfo, /*isTouchedWindow=*/true));
     }
     return info;
 }
 
-std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
-                                                         bool isTouchedWindow) const {
-    return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
-                                "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
-                                "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
-                                "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
-                        isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
-                        info->ownerUid.toString().c_str(), info->id,
-                        toString(info->touchOcclusionMode).c_str(), info->alpha, info->frame.left,
-                        info->frame.top, info->frame.right, info->frame.bottom,
-                        dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
-                        info->inputConfig.string().c_str(), toString(info->token != nullptr),
-                        info->applicationInfo.name.c_str(),
-                        binderToString(info->applicationInfo.token).c_str());
-}
-
-bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+bool InputDispatcher::DispatcherWindowInfo::isTouchTrusted(
+        const TouchOcclusionInfo& occlusionInfo) const {
     if (occlusionInfo.hasBlockingOcclusion) {
         ALOGW("Untrusted touch due to occlusion by %s/%s", occlusionInfo.obscuringPackage.c_str(),
               occlusionInfo.obscuringUid.toString().c_str());
@@ -3172,29 +3172,27 @@
     return true;
 }
 
-bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
-                                                    float x, float y) const {
+bool InputDispatcher::DispatcherWindowInfo::isWindowObscuredAtPoint(
+        const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
     ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            mWindowInfos.getWindowHandlesForDisplay(displayId);
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
     for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
         }
         const WindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) &&
-            windowOccludesTouchAt(*otherInfo, displayId, x, y,
-                                  mWindowInfos.getDisplayTransform(displayId))) {
+            windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId))) {
             return true;
         }
     }
     return false;
 }
 
-bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const {
+bool InputDispatcher::DispatcherWindowInfo::isWindowObscured(
+        const sp<WindowInfoHandle>& windowHandle) const {
     ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            mWindowInfos.getWindowHandlesForDisplay(displayId);
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
     const WindowInfo* windowInfo = windowHandle->getInfo();
     for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
@@ -4858,6 +4856,10 @@
                 flags |= AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
             }
 
+            if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL) {
+                flags |= AMOTION_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+            }
+
             mLock.lock();
 
             if (shouldRejectInjectedMotionLocked(motionEvent, resolvedDeviceId, displayId,
@@ -5264,8 +5266,9 @@
     return dump;
 }
 
-bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
-                                                   const MotionEntry& motionEntry) const {
+bool InputDispatcher::canWindowReceiveMotionLocked(
+        const sp<android::gui::WindowInfoHandle>& window,
+        const android::inputdispatcher::MotionEntry& motionEntry) const {
     const WindowInfo& info = *window->getInfo();
 
     // Skip spy window targets that are not valid for targeted injection.
@@ -5298,8 +5301,9 @@
 
     // Drop events that can't be trusted due to occlusion
     const auto [x, y] = resolveTouchedPosition(motionEntry);
-    TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y);
-    if (!isTouchTrustedLocked(occlusionInfo)) {
+    DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
+            mWindowInfos.computeTouchOcclusionInfo(window, x, y);
+    if (!mWindowInfos.isTouchTrusted(occlusionInfo)) {
         if (DEBUG_TOUCH_OCCLUSION) {
             ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
             for (const auto& log : occlusionInfo.debugInfo) {
@@ -5743,13 +5747,8 @@
 }
 
 void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
-    if (opacity < 0 || opacity > 1) {
-        LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
-        return;
-    }
-
     std::scoped_lock lock(mLock);
-    mMaximumObscuringOpacityForTouch = opacity;
+    mWindowInfos.setMaximumObscuringOpacityForTouch(opacity);
 }
 
 std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
@@ -7043,7 +7042,7 @@
     if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) ||
         (windowHandle->getInfo()->inputConfig.test(
                  WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
-         isWindowObscuredLocked(windowHandle))) {
+         mWindowInfos.isWindowObscured(windowHandle))) {
         ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
               "display %s.",
               ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
@@ -7096,7 +7095,7 @@
     const sp<WindowInfoHandle> oldWallpaper =
             oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
     const sp<WindowInfoHandle> newWallpaper =
-            newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr;
+            newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
     if (oldWallpaper == newWallpaper) {
         return;
     }
@@ -7133,7 +7132,7 @@
     const sp<WindowInfoHandle> oldWallpaper =
             oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
     const sp<WindowInfoHandle> newWallpaper =
-            newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr;
+            newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
     if (oldWallpaper == newWallpaper) {
         return;
     }
@@ -7165,10 +7164,10 @@
     }
 }
 
-sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow(
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWallpaperWindowBelow(
         const sp<WindowInfoHandle>& windowHandle) const {
     const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            mWindowInfos.getWindowHandlesForDisplay(windowHandle->getInfo()->displayId);
+            getWindowHandlesForDisplay(windowHandle->getInfo()->displayId);
     bool foundWindow = false;
     for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (!foundWindow && otherHandle != windowHandle) {
@@ -7349,4 +7348,11 @@
     return dump;
 }
 
+void InputDispatcher::DispatcherWindowInfo::setMaximumObscuringOpacityForTouch(float opacity) {
+    if (opacity < 0 || opacity > 1) {
+        LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+    }
+    mMaximumObscuringOpacityForTouch = opacity;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7bfa738..415f4c8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -224,6 +224,132 @@
     /** Stores the latest user-activity poke event times per user activity types. */
     std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock);
 
+    template <typename T>
+    struct StrongPointerHash {
+        std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
+    };
+
+    class ConnectionManager {
+    public:
+        ConnectionManager(const sp<Looper>& lopper);
+        ~ConnectionManager();
+
+        std::shared_ptr<Connection> getConnection(const sp<IBinder>& inputConnectionToken) const;
+
+        // Find a monitor pid by the provided token.
+        std::optional<gui::Pid> findMonitorPidByToken(const sp<IBinder>& token) const;
+        void forEachGlobalMonitorConnection(
+                std::function<void(const std::shared_ptr<Connection>&)> f) const;
+        void forEachGlobalMonitorConnection(
+                ui::LogicalDisplayId displayId,
+                std::function<void(const std::shared_ptr<Connection>&)> f) const;
+
+        void createGlobalInputMonitor(ui::LogicalDisplayId displayId,
+                                      std::unique_ptr<InputChannel>&& inputChannel,
+                                      const IdGenerator& idGenerator, gui::Pid pid,
+                                      std::function<int(int)> callback);
+
+        status_t removeConnection(const std::shared_ptr<Connection>& connection);
+
+        void createConnection(std::unique_ptr<InputChannel>&& inputChannel,
+                              const IdGenerator& idGenerator, std::function<int(int)> callback);
+
+        std::string dump(nsecs_t currentTime) const;
+
+    private:
+        const sp<Looper> mLooper;
+
+        // All registered connections mapped by input channel token.
+        std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>>
+                mConnectionsByToken;
+
+        // Input channels that will receive a copy of all input events sent to the provided display.
+        std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay;
+
+        void removeMonitorChannel(const sp<IBinder>& connectionToken);
+    };
+
+    ConnectionManager mConnectionManager GUARDED_BY(mLock);
+
+    class DispatcherWindowInfo {
+    public:
+        struct TouchOcclusionInfo {
+            bool hasBlockingOcclusion;
+            float obscuringOpacity;
+            std::string obscuringPackage;
+            gui::Uid obscuringUid = gui::Uid::INVALID;
+            std::vector<std::string> debugInfo;
+        };
+
+        void setWindowHandlesForDisplay(
+                ui::LogicalDisplayId displayId,
+                std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles);
+
+        void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos);
+
+        void removeDisplay(ui::LogicalDisplayId displayId);
+
+        void setMaximumObscuringOpacityForTouch(float opacity);
+
+        // Get a reference to window handles by display, return an empty vector if not found.
+        const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
+                ui::LogicalDisplayId displayId) const;
+
+        void forEachWindowHandle(
+                std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const;
+
+        void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const;
+
+        // Get the transform for display, returns Identity-transform if display is missing.
+        ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
+
+        // Get the raw transform to use for motion events going to the given window.
+        ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+
+        // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
+        // display-id is not provided lookup is done for all displays.
+        sp<android::gui::WindowInfoHandle> findWindowHandle(
+                const sp<IBinder>& windowHandleToken,
+                std::optional<ui::LogicalDisplayId> displayId = {}) const;
+
+        bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+        // Returns the touched window at the given location, excluding the ignoreWindow if provided.
+        sp<android::gui::WindowInfoHandle> findTouchedWindowAt(
+                ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
+                const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const;
+
+        std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt(
+                ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
+                const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay)
+                const;
+
+        TouchOcclusionInfo computeTouchOcclusionInfo(
+                const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const;
+
+        bool isWindowObscured(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+        bool isWindowObscuredAtPoint(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                                     float x, float y) const;
+
+        sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
+                const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+        bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const;
+
+        std::string dumpDisplayAndWindowInfo() const;
+
+    private:
+        std::unordered_map<ui::LogicalDisplayId /*displayId*/,
+                           std::vector<sp<android::gui::WindowInfoHandle>>>
+                mWindowHandlesByDisplay;
+        std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
+                mDisplayInfos;
+        float mMaximumObscuringOpacityForTouch{1.0f};
+    };
+
+    DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
+
     // With each iteration, InputDispatcher nominally processes one queued event,
     // a timeout, or a response from an input consumer.
     // This method should only be called on the input dispatcher's own thread.
@@ -262,11 +388,6 @@
 
     status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
-    template <typename T>
-    struct StrongPointerHash {
-        std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
-    };
-
     const HmacKeyManager mHmacKeyManager;
     const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry,
                                                const DispatchEntry& dispatchEntry) const;
@@ -326,7 +447,6 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
-    float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
 
     // This map is not really needed, but it helps a lot with debugging (dumpsys input).
     // In the java layer, touch mode states are spread across multiple DisplayContent objects,
@@ -344,103 +464,6 @@
     };
     sp<gui::WindowInfosListener> mWindowInfoListener;
 
-    class DispatcherWindowInfo {
-    public:
-        void setWindowHandlesForDisplay(
-                ui::LogicalDisplayId displayId,
-                std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles);
-
-        void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos);
-
-        void removeDisplay(ui::LogicalDisplayId displayId);
-
-        // Get a reference to window handles by display, return an empty vector if not found.
-        const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
-                ui::LogicalDisplayId displayId) const;
-
-        void forEachWindowHandle(
-                std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const;
-
-        void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const;
-
-        // Get the transform for display, returns Identity-transform if display is missing.
-        ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
-
-        // Get the raw transform to use for motion events going to the given window.
-        ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
-
-        // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
-        // display-id is not provided lookup is done for all displays.
-        sp<android::gui::WindowInfoHandle> findWindowHandle(
-                const sp<IBinder>& windowHandleToken,
-                std::optional<ui::LogicalDisplayId> displayId = {}) const;
-
-        bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
-
-        // Returns the touched window at the given location, excluding the ignoreWindow if provided.
-        sp<android::gui::WindowInfoHandle> findTouchedWindowAt(
-                ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
-                const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const;
-
-        std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt(
-                ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
-                const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay)
-                const;
-
-        std::string dumpDisplayAndWindowInfo() const;
-
-    private:
-        std::unordered_map<ui::LogicalDisplayId /*displayId*/,
-                           std::vector<sp<android::gui::WindowInfoHandle>>>
-                mWindowHandlesByDisplay;
-        std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
-                mDisplayInfos;
-    };
-
-    DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
-
-    class ConnectionManager {
-    public:
-        ConnectionManager(const sp<Looper>& lopper);
-        ~ConnectionManager();
-
-        std::shared_ptr<Connection> getConnection(const sp<IBinder>& inputConnectionToken) const;
-
-        // Find a monitor pid by the provided token.
-        std::optional<gui::Pid> findMonitorPidByToken(const sp<IBinder>& token) const;
-        void forEachGlobalMonitorConnection(
-                std::function<void(const std::shared_ptr<Connection>&)> f) const;
-        void forEachGlobalMonitorConnection(
-                ui::LogicalDisplayId displayId,
-                std::function<void(const std::shared_ptr<Connection>&)> f) const;
-
-        void createGlobalInputMonitor(ui::LogicalDisplayId displayId,
-                                      std::unique_ptr<InputChannel>&& inputChannel,
-                                      const IdGenerator& idGenerator, gui::Pid pid,
-                                      std::function<int(int)> callback);
-
-        status_t removeConnection(const std::shared_ptr<Connection>& connection);
-
-        void createConnection(std::unique_ptr<InputChannel>&& inputChannel,
-                              const IdGenerator& idGenerator, std::function<int(int)> callback);
-
-        std::string dump(nsecs_t currentTime) const;
-
-    private:
-        const sp<Looper> mLooper;
-
-        // All registered connections mapped by input channel token.
-        std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>>
-                mConnectionsByToken;
-
-        // Input channels that will receive a copy of all input events sent to the provided display.
-        std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay;
-
-        void removeMonitorChannel(const sp<IBinder>& connectionToken);
-    };
-
-    ConnectionManager mConnectionManager GUARDED_BY(mLock);
-
     void setInputWindowsLocked(
             const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
             ui::LogicalDisplayId displayId) REQUIRES(mLock);
@@ -626,24 +649,6 @@
     void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
     void finishDragAndDrop(ui::LogicalDisplayId displayId, float x, float y) REQUIRES(mLock);
 
-    struct TouchOcclusionInfo {
-        bool hasBlockingOcclusion;
-        float obscuringOpacity;
-        std::string obscuringPackage;
-        gui::Uid obscuringUid = gui::Uid::INVALID;
-        std::vector<std::string> debugInfo;
-    };
-
-    TouchOcclusionInfo computeTouchOcclusionInfoLocked(
-            const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const
-            REQUIRES(mLock);
-    bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
-    bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                       float x, float y) const REQUIRES(mLock);
-    bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const
-            REQUIRES(mLock);
-    std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info,
-                                            bool isTouchWindow) const;
     std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
                                           const sp<android::gui::WindowInfoHandle>& windowHandle);
 
@@ -796,9 +801,6 @@
                                 const std::unique_ptr<trace::EventTrackerInterface>& traceTracker)
             REQUIRES(mLock);
 
-    sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
-            const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
-
     /** Stores the value of the input flag for per device input latency metrics. */
     const bool mPerDeviceInputLatencyMetricsFlag =
             com::android::input::flags::enable_per_device_input_latency_metrics();
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
index eb33e2b..c19d278 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -41,6 +41,16 @@
                                   .matchImeConnectionActive = {}}},
 };
 
+template <typename Pointer>
+void writeAxisValue(Pointer* pointer, int32_t axis, float value, bool isRedacted) {
+    auto* axisEntry = pointer->add_axis_value();
+    axisEntry->set_axis(axis);
+
+    if (!isRedacted) {
+        axisEntry->set_value(value);
+    }
+}
+
 } // namespace internal
 
 /**
@@ -85,14 +95,20 @@
 
             const auto& coords = event.pointerCoords[i];
             auto bits = BitSet64(coords.bits);
+
+            if (isFromSource(event.source, AINPUT_SOURCE_CLASS_POINTER)) {
+                // Always include the X and Y axes for pointer events, since the
+                // bits will not be marked if the value is 0.
+                for (const auto axis : {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}) {
+                    if (!bits.hasBit(axis)) {
+                        internal::writeAxisValue(pointer, axis, 0.0f, isRedacted);
+                    }
+                }
+            }
+
             for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
                 const auto axis = bits.clearFirstMarkedBit();
-                auto axisEntry = pointer->add_axis_value();
-                axisEntry->set_axis(axis);
-
-                if (!isRedacted) {
-                    axisEntry->set_value(coords.values[axisIndex]);
-                }
+                internal::writeAxisValue(pointer, axis, coords.values[axisIndex], isRedacted);
             }
         }
     }
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 823f8a5..2ff6e1c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -50,7 +50,7 @@
     uint32_t policyFlags;
     int32_t deviceId;
     uint32_t source;
-    ui::LogicalDisplayId displayId;
+    ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
     int32_t action;
     int32_t keyCode;
     int32_t scanCode;
@@ -108,7 +108,7 @@
     TracedEvent eventEntry;
     nsecs_t deliveryTime;
     int32_t resolvedFlags;
-    gui::Uid targetUid;
+    gui::Uid targetUid = gui::Uid::INVALID;
     int64_t vsyncId;
     int32_t windowId;
     ui::Transform transform;
diff --git a/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp
index 414da66..1fd6cee 100644
--- a/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp
+++ b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp
@@ -60,9 +60,43 @@
     MOCK_METHOD(MockProtoPointer*, add_pointer, ());
 };
 
-using TestProtoConverter = AndroidInputEventProtoConverter<MockProtoMotion, proto::AndroidKeyEvent,
-                                                           proto::AndroidWindowInputDispatchEvent,
-                                                           proto::AndroidInputEventConfig::Decoder>;
+class MockProtoKey {
+public:
+    MOCK_METHOD(void, set_event_id, (uint32_t));
+    MOCK_METHOD(void, set_event_time_nanos, (int64_t));
+    MOCK_METHOD(void, set_down_time_nanos, (int64_t));
+    MOCK_METHOD(void, set_source, (uint32_t));
+    MOCK_METHOD(void, set_action, (int32_t));
+    MOCK_METHOD(void, set_device_id, (uint32_t));
+    MOCK_METHOD(void, set_display_id, (uint32_t));
+    MOCK_METHOD(void, set_repeat_count, (uint32_t));
+    MOCK_METHOD(void, set_flags, (uint32_t));
+    MOCK_METHOD(void, set_policy_flags, (uint32_t));
+    MOCK_METHOD(void, set_key_code, (uint32_t));
+    MOCK_METHOD(void, set_scan_code, (uint32_t));
+    MOCK_METHOD(void, set_meta_state, (uint32_t));
+};
+
+class MockProtoDispatchPointer {
+public:
+    MOCK_METHOD(void, set_pointer_id, (uint32_t));
+    MOCK_METHOD(void, set_x_in_display, (float));
+    MOCK_METHOD(void, set_y_in_display, (float));
+    MOCK_METHOD(MockProtoAxisValue*, add_axis_value_in_window, ());
+};
+
+class MockProtoDispatch {
+public:
+    MOCK_METHOD(void, set_event_id, (uint32_t));
+    MOCK_METHOD(void, set_vsync_id, (uint32_t));
+    MOCK_METHOD(void, set_window_id, (uint32_t));
+    MOCK_METHOD(void, set_resolved_flags, (uint32_t));
+    MOCK_METHOD(MockProtoDispatchPointer*, add_dispatched_pointer, ());
+};
+
+using TestProtoConverter =
+        AndroidInputEventProtoConverter<MockProtoMotion, MockProtoKey, MockProtoDispatch,
+                                        proto::AndroidInputEventConfig::Decoder>;
 
 TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent) {
     TracedMotionEvent event{};
@@ -256,6 +290,297 @@
     TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/true);
 }
 
+// Test any special handling for zero values for pointer events.
+TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent_ZeroValues) {
+    TracedMotionEvent event{};
+    event.id = 0;
+    event.eventTime = 0;
+    event.downTime = 0;
+    event.source = AINPUT_SOURCE_MOUSE;
+    event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS;
+    event.deviceId = 0;
+    event.displayId = ui::LogicalDisplayId(0);
+    event.classification = {};
+    event.flags = 0;
+    event.policyFlags = 0;
+    event.buttonState = 0;
+    event.actionButton = 0;
+    event.xCursorPosition = 0.0f;
+    event.yCursorPosition = 0.0f;
+    event.metaState = 0;
+    event.xPrecision = 0.0f;
+    event.yPrecision = 0.0f;
+    event.pointerProperties.emplace_back(PointerProperties{
+            .id = 0,
+            .toolType = ToolType::MOUSE,
+    });
+    event.pointerProperties.emplace_back(PointerProperties{
+            .id = 1,
+            .toolType = ToolType::FINGER,
+    });
+    // Zero values for x and y axes are always traced for pointer events.
+    // However, zero values for other axes may not necessarily be traced.
+    event.pointerCoords.emplace_back();
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f);
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 1.0f);
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+    event.pointerCoords.emplace_back();
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f);
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 0.0f);
+    event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+
+    testing::StrictMock<MockProtoMotion> proto;
+    testing::StrictMock<MockProtoPointer> pointer1;
+    testing::StrictMock<MockProtoPointer> pointer2;
+    testing::StrictMock<MockProtoAxisValue> axisValue1;
+    testing::StrictMock<MockProtoAxisValue> axisValue2;
+    testing::StrictMock<MockProtoAxisValue> axisValue3;
+    testing::StrictMock<MockProtoAxisValue> axisValue4;
+
+    EXPECT_CALL(proto, set_event_id(0));
+    EXPECT_CALL(proto, set_event_time_nanos(0));
+    EXPECT_CALL(proto, set_down_time_nanos(0));
+    EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE));
+    EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+    EXPECT_CALL(proto, set_device_id(0));
+    EXPECT_CALL(proto, set_display_id(0));
+    EXPECT_CALL(proto, set_classification(0));
+    EXPECT_CALL(proto, set_flags(0));
+    EXPECT_CALL(proto, set_policy_flags(0));
+    EXPECT_CALL(proto, set_button_state(0));
+    EXPECT_CALL(proto, set_action_button(0));
+    EXPECT_CALL(proto, set_cursor_position_x(0.0f));
+    EXPECT_CALL(proto, set_cursor_position_y(0.0f));
+    EXPECT_CALL(proto, set_meta_state(0));
+    EXPECT_CALL(proto, set_precision_x(0.0f));
+    EXPECT_CALL(proto, set_precision_y(0.0f));
+
+    EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2));
+
+    EXPECT_CALL(pointer1, set_pointer_id(0));
+    EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE));
+    EXPECT_CALL(pointer1, add_axis_value())
+            .WillOnce(Return(&axisValue1))
+            .WillOnce(Return(&axisValue2));
+    EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X));
+    EXPECT_CALL(axisValue1, set_value(0.0f));
+    EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y));
+    EXPECT_CALL(axisValue2, set_value(1.0f));
+
+    EXPECT_CALL(pointer2, set_pointer_id(1));
+    EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER));
+    EXPECT_CALL(pointer2, add_axis_value())
+            .WillOnce(Return(&axisValue3))
+            .WillOnce(Return(&axisValue4));
+    EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_X));
+    EXPECT_CALL(axisValue3, set_value(0.0f));
+    EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_Y));
+    EXPECT_CALL(axisValue4, set_value(0.0f));
+
+    TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent) {
+    TracedKeyEvent event{};
+    event.id = 1;
+    event.eventTime = 2;
+    event.downTime = 3;
+    event.source = AINPUT_SOURCE_KEYBOARD;
+    event.action = AKEY_EVENT_ACTION_DOWN;
+    event.deviceId = 4;
+    event.displayId = ui::LogicalDisplayId(5);
+    event.repeatCount = 6;
+    event.flags = 7;
+    event.policyFlags = 8;
+    event.keyCode = 9;
+    event.scanCode = 10;
+    event.metaState = 11;
+
+    testing::StrictMock<MockProtoKey> proto;
+
+    EXPECT_CALL(proto, set_event_id(1));
+    EXPECT_CALL(proto, set_event_time_nanos(2));
+    EXPECT_CALL(proto, set_down_time_nanos(3));
+    EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD));
+    EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN));
+    EXPECT_CALL(proto, set_device_id(4));
+    EXPECT_CALL(proto, set_display_id(5));
+    EXPECT_CALL(proto, set_repeat_count(6));
+    EXPECT_CALL(proto, set_flags(7));
+    EXPECT_CALL(proto, set_policy_flags(8));
+    EXPECT_CALL(proto, set_key_code(9));
+    EXPECT_CALL(proto, set_scan_code(10));
+    EXPECT_CALL(proto, set_meta_state(11));
+
+    TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent_Redacted) {
+    TracedKeyEvent event{};
+    event.id = 1;
+    event.eventTime = 2;
+    event.downTime = 3;
+    event.source = AINPUT_SOURCE_KEYBOARD;
+    event.action = AKEY_EVENT_ACTION_DOWN;
+    event.deviceId = 4;
+    event.displayId = ui::LogicalDisplayId(5);
+    event.repeatCount = 6;
+    event.flags = 7;
+    event.policyFlags = 8;
+    event.keyCode = 9;
+    event.scanCode = 10;
+    event.metaState = 11;
+
+    testing::StrictMock<MockProtoKey> proto;
+
+    EXPECT_CALL(proto, set_event_id(1));
+    EXPECT_CALL(proto, set_event_time_nanos(2));
+    EXPECT_CALL(proto, set_down_time_nanos(3));
+    EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD));
+    EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN));
+    EXPECT_CALL(proto, set_device_id(4));
+    EXPECT_CALL(proto, set_display_id(5));
+    EXPECT_CALL(proto, set_repeat_count(6));
+    EXPECT_CALL(proto, set_flags(7));
+    EXPECT_CALL(proto, set_policy_flags(8));
+
+    // Redacted fields
+    EXPECT_CALL(proto, set_key_code(_)).Times(0);
+    EXPECT_CALL(proto, set_scan_code(_)).Times(0);
+    EXPECT_CALL(proto, set_meta_state(_)).Times(0);
+
+    TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/true);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_IdentityTransform) {
+    TracedMotionEvent motion{};
+    motion.pointerProperties.emplace_back(PointerProperties{
+            .id = 4,
+            .toolType = ToolType::MOUSE,
+    });
+    motion.pointerCoords.emplace_back();
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f);
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+    WindowDispatchArgs args{};
+    args.eventEntry = motion;
+    args.vsyncId = 1;
+    args.windowId = 2;
+    args.resolvedFlags = 3;
+    args.rawTransform = ui::Transform{};
+    args.transform = ui::Transform{};
+
+    testing::StrictMock<MockProtoDispatch> proto;
+    testing::StrictMock<MockProtoDispatchPointer> pointer;
+
+    EXPECT_CALL(proto, set_event_id(0));
+    EXPECT_CALL(proto, set_vsync_id(1));
+    EXPECT_CALL(proto, set_window_id(2));
+    EXPECT_CALL(proto, set_resolved_flags(3));
+    EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer));
+    EXPECT_CALL(pointer, set_pointer_id(4));
+
+    // Since we are using identity transforms, the axis values will be identical to those in the
+    // traced event, so they should not be traced here.
+    EXPECT_CALL(pointer, add_axis_value_in_window()).Times(0);
+    EXPECT_CALL(pointer, set_x_in_display(_)).Times(0);
+    EXPECT_CALL(pointer, set_y_in_display(_)).Times(0);
+
+    TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_CustomTransform) {
+    TracedMotionEvent motion{};
+    motion.pointerProperties.emplace_back(PointerProperties{
+            .id = 4,
+            .toolType = ToolType::MOUSE,
+    });
+    motion.pointerCoords.emplace_back();
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 8.0f);
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+    WindowDispatchArgs args{};
+    args.eventEntry = motion;
+    args.vsyncId = 1;
+    args.windowId = 2;
+    args.resolvedFlags = 3;
+    args.rawTransform.set(2, 0, 0, 0.5);
+    args.transform.set(1.0, 0, 0, 0.5);
+
+    testing::StrictMock<MockProtoDispatch> proto;
+    testing::StrictMock<MockProtoDispatchPointer> pointer;
+    testing::StrictMock<MockProtoAxisValue> axisValue1;
+
+    EXPECT_CALL(proto, set_event_id(0));
+    EXPECT_CALL(proto, set_vsync_id(1));
+    EXPECT_CALL(proto, set_window_id(2));
+    EXPECT_CALL(proto, set_resolved_flags(3));
+    EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer));
+    EXPECT_CALL(pointer, set_pointer_id(4));
+
+    // Only the transformed axis-values that differ from the traced event will be traced.
+    EXPECT_CALL(pointer, add_axis_value_in_window()).WillOnce(Return(&axisValue1));
+    EXPECT_CALL(pointer, set_x_in_display(16.0f)); // MotionEvent::getRawX
+    EXPECT_CALL(pointer, set_y_in_display(3.0f));  // MotionEvent::getRawY
+
+    EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_Y));
+    EXPECT_CALL(axisValue1, set_value(3.0f));
+
+    TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_Redacted) {
+    TracedMotionEvent motion{};
+    motion.pointerProperties.emplace_back(PointerProperties{
+            .id = 4,
+            .toolType = ToolType::MOUSE,
+    });
+    motion.pointerCoords.emplace_back();
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f);
+    motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+    WindowDispatchArgs args{};
+    args.eventEntry = motion;
+    args.vsyncId = 1;
+    args.windowId = 2;
+    args.resolvedFlags = 3;
+    args.rawTransform = ui::Transform{};
+    args.transform = ui::Transform{};
+
+    testing::StrictMock<MockProtoDispatch> proto;
+
+    EXPECT_CALL(proto, set_event_id(0));
+    EXPECT_CALL(proto, set_vsync_id(1));
+    EXPECT_CALL(proto, set_window_id(2));
+    EXPECT_CALL(proto, set_resolved_flags(3));
+
+    // Redacted fields
+    EXPECT_CALL(proto, add_dispatched_pointer()).Times(0);
+
+    TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Key) {
+    TracedKeyEvent key{};
+
+    WindowDispatchArgs args{};
+    args.eventEntry = key;
+    args.vsyncId = 1;
+    args.windowId = 2;
+    args.resolvedFlags = 3;
+    args.rawTransform = ui::Transform{};
+    args.transform = ui::Transform{};
+
+    testing::StrictMock<MockProtoDispatch> proto;
+
+    EXPECT_CALL(proto, set_event_id(0));
+    EXPECT_CALL(proto, set_vsync_id(1));
+    EXPECT_CALL(proto, set_window_id(2));
+    EXPECT_CALL(proto, set_resolved_flags(3));
+
+    TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true);
+}
+
 } // namespace
 
 } // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 6b4c4b7..368db1b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -8101,6 +8101,17 @@
     ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
     ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
     ASSERT_EQ(0, verifiedKey.repeatCount);
+
+    // InputEvent and subclasses don't have a virtual destructor and only
+    // InputEvent's destructor gets called when `verified` goes out of scope,
+    // even if `verifyInputEvent` returns an object of a subclass.  To fix this,
+    // we should either consider using std::variant in some way, or introduce an
+    // intermediate POD data structure that we will put the data into just prior
+    // to signing.  Adding virtual functions to these classes is undesirable as
+    // the bytes in these objects are getting signed.  As a temporary fix, cast
+    // the pointer to the correct class (which is statically known) before
+    // destruction.
+    delete (VerifiedKeyEvent*)verified.release();
 }
 
 TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
@@ -8148,6 +8159,10 @@
     EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
     EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
+
+    // Cast to the correct type before destruction.  See explanation at the end
+    // of the VerifyInputEvent_KeyEvent test.
+    delete (VerifiedMotionEvent*)verified.release();
 }
 
 /**
@@ -9890,6 +9905,15 @@
                        AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
 }
 
+TEST_F(InputFilterInjectionPolicyTest,
+       MotionEventsInjectedFromAccessibilityTool_HaveAccessibilityFlags) {
+    testInjectedMotion(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY |
+                               POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL,
+                       /*injectedDeviceId=*/3, /*resolvedDeviceId=*/3,
+                       AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT |
+                               AMOTION_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL);
+}
+
 TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
     testInjectedKey(/*policyFlags=*/0, /*injectedDeviceId=*/3,
                     /*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e1c5383..d117753 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1070,7 +1070,8 @@
 void SurfaceFlinger::initBootProperties() {
     property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0");
 
-    if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) {
+    if (base::GetBoolProperty("debug.sf.boot_animation"s, true) &&
+        (base::GetIntProperty("debug.sf.nobootanimation"s, 0) == 0)) {
         // Reset and (if needed) start BootAnimation.
         property_set("service.bootanim.exit", "0");
         property_set("service.bootanim.progress", "0");