Merge "Add Razer and Glorious O2 wireless mice to blocklist" into main
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 4486bd6..db56551 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -119,7 +119,6 @@
  */
 static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
 static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/";
-static constexpr const char* kMntFuse = "/mnt/pass_through/0/";
 
 static std::atomic<bool> sAppDataIsolationEnabled(false);
 
@@ -3697,7 +3696,9 @@
         std::getline(in, ignored);
 
         if (android::base::GetBoolProperty(kFuseProp, false)) {
-            if (target.find(kMntFuse) == 0) {
+            const std::regex kMntFuseRe =
+                    std::regex(R"(^/mnt/pass_through/(0|[0-9]+/[A-Z0-9]{4}-[A-Z0-9]{4}).*)");
+            if (std::regex_match(target, kMntFuseRe)) {
                 LOG(DEBUG) << "Found storage mount " << source << " at " << target;
                 mStorageMounts[source] = target;
             }
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c86adef..c818e0d 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <linux/unistd.h>
+#include <sched.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c0ebee0..bc027d7 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -616,6 +616,7 @@
         }
 #else
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
         return INVALID_OPERATION;
 #endif // BINDER_WITH_KERNEL_IPC
     } else {
@@ -797,6 +798,7 @@
         setDataPosition(initPosition);
 #else
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
 #endif
     } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) {
         for (const auto& fd : *rpcFields->mFds) {
@@ -839,9 +841,10 @@
         }
 #else
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
         return INVALID_OPERATION;
 #endif // BINDER_WITH_KERNEL_IPC
-    } else if (const auto* rpcFields = maybeRpcFields()) {
+    } else if (maybeRpcFields()) {
         return INVALID_OPERATION;
     }
     return NO_ERROR;
@@ -879,6 +882,7 @@
         }
 #else
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
         return INVALID_OPERATION;
 #endif // BINDER_WITH_KERNEL_IPC
     } else if (const auto* rpcFields = maybeRpcFields()) {
@@ -971,6 +975,7 @@
         writeInt32(kHeader);
 #else  // BINDER_WITH_KERNEL_IPC
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
         return INVALID_OPERATION;
 #endif // BINDER_WITH_KERNEL_IPC
     }
@@ -1061,6 +1066,7 @@
 #else  // BINDER_WITH_KERNEL_IPC
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
         (void)threadState;
+        (void)kernelFields;
         return false;
 #endif // BINDER_WITH_KERNEL_IPC
     }
@@ -2688,6 +2694,7 @@
 #else  // BINDER_WITH_KERNEL_IPC
         (void)newObjectsSize;
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+        (void)kernelFields;
 #endif // BINDER_WITH_KERNEL_IPC
     } else if (auto* rpcFields = maybeRpcFields()) {
         rpcFields->mFds.reset();
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index 99fa6b8..51b9716b 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -20,6 +20,7 @@
 #include <condition_variable>
 #include <functional>
 #include <memory>
+#include <mutex>
 #include <thread>
 
 #include <binder/Common.h>
diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs
index 58ea138..d71ed73 100644
--- a/libs/binder/rust/src/persistable_bundle.rs
+++ b/libs/binder/rust/src/persistable_bundle.rs
@@ -22,13 +22,18 @@
 };
 use binder_ndk_sys::{
     APersistableBundle, APersistableBundle_delete, APersistableBundle_dup,
-    APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getDouble,
-    APersistableBundle_getInt, APersistableBundle_getLong, APersistableBundle_isEqual,
-    APersistableBundle_new, APersistableBundle_putBoolean, APersistableBundle_putDouble,
-    APersistableBundle_putInt, APersistableBundle_putLong, APersistableBundle_readFromParcel,
-    APersistableBundle_size, APersistableBundle_writeToParcel,
+    APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanVector,
+    APersistableBundle_getDouble, APersistableBundle_getDoubleVector, APersistableBundle_getInt,
+    APersistableBundle_getIntVector, APersistableBundle_getLong, APersistableBundle_getLongVector,
+    APersistableBundle_getPersistableBundle, APersistableBundle_isEqual, APersistableBundle_new,
+    APersistableBundle_putBoolean, APersistableBundle_putBooleanVector,
+    APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt,
+    APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector,
+    APersistableBundle_putPersistableBundle, APersistableBundle_putString,
+    APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size,
+    APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_KEY_NOT_FOUND,
 };
-use std::ffi::{CString, NulError};
+use std::ffi::{c_char, CString, NulError};
 use std::ptr::{null_mut, NonNull};
 
 /// A mapping from string keys to values of various types.
@@ -60,7 +65,7 @@
         let key = CString::new(key)?;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`.
+        // to be valid for the duration of this call.
         Ok(unsafe { APersistableBundle_erase(self.0.as_ptr(), key.as_ptr()) != 0 })
     }
 
@@ -73,7 +78,7 @@
         let key = CString::new(key)?;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`.
+        // to be valid for the duration of this call.
         unsafe {
             APersistableBundle_putBoolean(self.0.as_ptr(), key.as_ptr(), value);
         }
@@ -89,7 +94,7 @@
         let key = CString::new(key)?;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`.
+        // to be valid for the duration of this call.
         unsafe {
             APersistableBundle_putInt(self.0.as_ptr(), key.as_ptr(), value);
         }
@@ -105,7 +110,7 @@
         let key = CString::new(key)?;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`.
+        // to be valid for the duration of this call.
         unsafe {
             APersistableBundle_putLong(self.0.as_ptr(), key.as_ptr(), value);
         }
@@ -121,13 +126,182 @@
         let key = CString::new(key)?;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`.
+        // to be valid for the duration of this call.
         unsafe {
             APersistableBundle_putDouble(self.0.as_ptr(), key.as_ptr(), value);
         }
         Ok(())
     }
 
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key or value contains a NUL character.
+    pub fn insert_string(&mut self, key: &str, value: &str) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        let value = CString::new(value)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `CStr::as_ptr` is guaranteed
+        // to be valid for the duration of this call.
+        unsafe {
+            APersistableBundle_putString(self.0.as_ptr(), key.as_ptr(), value.as_ptr());
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_bool_vec(&mut self, key: &str, value: &[bool]) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the duration of this call, and likewise the pointer returned by
+        // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+        // duration of the call.
+        unsafe {
+            APersistableBundle_putBooleanVector(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                value.as_ptr(),
+                value.len().try_into().unwrap(),
+            );
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_int_vec(&mut self, key: &str, value: &[i32]) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the duration of this call, and likewise the pointer returned by
+        // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+        // duration of the call.
+        unsafe {
+            APersistableBundle_putIntVector(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                value.as_ptr(),
+                value.len().try_into().unwrap(),
+            );
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_long_vec(&mut self, key: &str, value: &[i64]) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the duration of this call, and likewise the pointer returned by
+        // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+        // duration of the call.
+        unsafe {
+            APersistableBundle_putLongVector(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                value.as_ptr(),
+                value.len().try_into().unwrap(),
+            );
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_double_vec(&mut self, key: &str, value: &[f64]) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the duration of this call, and likewise the pointer returned by
+        // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+        // duration of the call.
+        unsafe {
+            APersistableBundle_putDoubleVector(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                value.as_ptr(),
+                value.len().try_into().unwrap(),
+            );
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_string_vec<'a, T: ToString + 'a>(
+        &mut self,
+        key: &str,
+        value: impl IntoIterator<Item = &'a T>,
+    ) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // We need to collect the new `CString`s into something first so that they live long enough
+        // for their pointers to be valid for the `APersistableBundle_putStringVector` call below.
+        let c_strings = value
+            .into_iter()
+            .map(|s| CString::new(s.to_string()))
+            .collect::<Result<Vec<_>, NulError>>()?;
+        let char_pointers = c_strings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the duration of this call, and likewise the pointer returned by
+        // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the
+        // duration of the call.
+        unsafe {
+            APersistableBundle_putStringVector(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                char_pointers.as_ptr(),
+                char_pointers.len().try_into().unwrap(),
+            );
+        }
+        Ok(())
+    }
+
+    /// Inserts a key-value pair into the bundle.
+    ///
+    /// If the key is already present then its value will be overwritten by the given value.
+    ///
+    /// Returns an error if the key contains a NUL character.
+    pub fn insert_persistable_bundle(
+        &mut self,
+        key: &str,
+        value: &PersistableBundle,
+    ) -> Result<(), NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`s. The pointer returned by `CStr::as_ptr` is
+        // guaranteed to be valid for the duration of this call, and
+        // `APersistableBundle_putPersistableBundle` does a deep copy so that is all that is
+        // required.
+        unsafe {
+            APersistableBundle_putPersistableBundle(
+                self.0.as_ptr(),
+                key.as_ptr(),
+                value.0.as_ptr(),
+            );
+        }
+        Ok(())
+    }
+
     /// Gets the boolean value associated with the given key.
     ///
     /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
@@ -137,8 +311,8 @@
         let mut value = false;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
-        // from a reference.
+        // to be valid for the duration of this call. The value pointer must be valid because it
+        // comes from a reference.
         if unsafe { APersistableBundle_getBoolean(self.0.as_ptr(), key.as_ptr(), &mut value) } {
             Ok(Some(value))
         } else {
@@ -155,8 +329,8 @@
         let mut value = 0;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
-        // from a reference.
+        // to be valid for the duration of this call. The value pointer must be valid because it
+        // comes from a reference.
         if unsafe { APersistableBundle_getInt(self.0.as_ptr(), key.as_ptr(), &mut value) } {
             Ok(Some(value))
         } else {
@@ -173,8 +347,8 @@
         let mut value = 0;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
-        // from a reference.
+        // to be valid for the duration of this call. The value pointer must be valid because it
+        // comes from a reference.
         if unsafe { APersistableBundle_getLong(self.0.as_ptr(), key.as_ptr(), &mut value) } {
             Ok(Some(value))
         } else {
@@ -191,14 +365,134 @@
         let mut value = 0.0;
         // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
         // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
-        // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
-        // from a reference.
+        // to be valid for the duration of this call. The value pointer must be valid because it
+        // comes from a reference.
         if unsafe { APersistableBundle_getDouble(self.0.as_ptr(), key.as_ptr(), &mut value) } {
             Ok(Some(value))
         } else {
             Ok(None)
         }
     }
+
+    /// Gets the vector of `T` associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    ///
+    /// `get_func` should be one of the `APersistableBundle_get*Vector` functions from
+    /// `binder_ndk_sys`.
+    ///
+    /// # Safety
+    ///
+    /// `get_func` must only require that the pointers it takes are valid for the duration of the
+    /// call. It must allow a null pointer for the buffer, and must return the size in bytes of
+    /// buffer it requires. If it is given a non-null buffer pointer it must write that number of
+    /// bytes to the buffer, which must be a whole number of valid `T` values.
+    unsafe fn get_vec<T: Clone + Default>(
+        &self,
+        key: &str,
+        get_func: unsafe extern "C" fn(
+            *const APersistableBundle,
+            *const c_char,
+            *mut T,
+            i32,
+        ) -> i32,
+    ) -> Result<Option<Vec<T>>, NulError> {
+        let key = CString::new(key)?;
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the lifetime of `key`. A null pointer is allowed for the buffer.
+        match unsafe { get_func(self.0.as_ptr(), key.as_ptr(), null_mut(), 0) } {
+            APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None),
+            required_buffer_size => {
+                let mut value = vec![
+                    T::default();
+                    usize::try_from(required_buffer_size).expect(
+                        "APersistableBundle_get*Vector returned invalid size"
+                    ) / size_of::<T>()
+                ];
+                // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for
+                // the lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()`
+                // is guaranteed to be valid for the lifetime of `key`. The value buffer pointer is
+                // valid as it comes from the Vec we just allocated.
+                match unsafe {
+                    get_func(
+                        self.0.as_ptr(),
+                        key.as_ptr(),
+                        value.as_mut_ptr(),
+                        (value.len() * size_of::<T>()).try_into().unwrap(),
+                    )
+                } {
+                    APERSISTABLEBUNDLE_KEY_NOT_FOUND => {
+                        panic!("APersistableBundle_get*Vector failed to find key after first finding it");
+                    }
+                    _ => Ok(Some(value)),
+                }
+            }
+        }
+    }
+
+    /// Gets the boolean vector value associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    pub fn get_bool_vec(&self, key: &str) -> Result<Option<Vec<bool>>, NulError> {
+        // SAFETY: APersistableBundle_getBooleanVector fulfils all the safety requirements of
+        // `get_vec`.
+        unsafe { self.get_vec(key, APersistableBundle_getBooleanVector) }
+    }
+
+    /// Gets the i32 vector value associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    pub fn get_int_vec(&self, key: &str) -> Result<Option<Vec<i32>>, NulError> {
+        // SAFETY: APersistableBundle_getIntVector fulfils all the safety requirements of
+        // `get_vec`.
+        unsafe { self.get_vec(key, APersistableBundle_getIntVector) }
+    }
+
+    /// Gets the i64 vector value associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    pub fn get_long_vec(&self, key: &str) -> Result<Option<Vec<i64>>, NulError> {
+        // SAFETY: APersistableBundle_getLongVector fulfils all the safety requirements of
+        // `get_vec`.
+        unsafe { self.get_vec(key, APersistableBundle_getLongVector) }
+    }
+
+    /// Gets the f64 vector value associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    pub fn get_double_vec(&self, key: &str) -> Result<Option<Vec<f64>>, NulError> {
+        // SAFETY: APersistableBundle_getDoubleVector fulfils all the safety requirements of
+        // `get_vec`.
+        unsafe { self.get_vec(key, APersistableBundle_getDoubleVector) }
+    }
+
+    /// Gets the `PersistableBundle` value associated with the given key.
+    ///
+    /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist
+    /// in the bundle.
+    pub fn get_persistable_bundle(&self, key: &str) -> Result<Option<Self>, NulError> {
+        let key = CString::new(key)?;
+        let mut value = null_mut();
+        // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the
+        // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed
+        // to be valid for the lifetime of `key`. The value pointer must be valid because it comes
+        // from a reference.
+        if unsafe {
+            APersistableBundle_getPersistableBundle(self.0.as_ptr(), key.as_ptr(), &mut value)
+        } {
+            Ok(Some(Self(NonNull::new(value).expect(
+                "APersistableBundle_getPersistableBundle returned true but didn't set outBundle",
+            ))))
+        } else {
+            Ok(None)
+        }
+    }
 }
 
 // SAFETY: The underlying *APersistableBundle can be moved between threads.
@@ -291,6 +585,10 @@
         assert_eq!(bundle.get_int("foo"), Ok(None));
         assert_eq!(bundle.get_long("foo"), Ok(None));
         assert_eq!(bundle.get_double("foo"), Ok(None));
+        assert_eq!(bundle.get_bool_vec("foo"), Ok(None));
+        assert_eq!(bundle.get_int_vec("foo"), Ok(None));
+        assert_eq!(bundle.get_long_vec("foo"), Ok(None));
+        assert_eq!(bundle.get_double_vec("foo"), Ok(None));
     }
 
     #[test]
@@ -339,4 +637,55 @@
         assert_eq!(bundle.get_double("double"), Ok(None));
         assert_eq!(bundle.size(), 0);
     }
+
+    #[test]
+    fn insert_string() {
+        let mut bundle = PersistableBundle::new();
+        assert_eq!(bundle.insert_string("string", "foo"), Ok(()));
+        assert_eq!(bundle.size(), 1);
+    }
+
+    #[test]
+    fn insert_get_vec() {
+        let mut bundle = PersistableBundle::new();
+
+        assert_eq!(bundle.insert_bool_vec("bool", &[]), Ok(()));
+        assert_eq!(bundle.insert_int_vec("int", &[42]), Ok(()));
+        assert_eq!(bundle.insert_long_vec("long", &[66, 67, 68]), Ok(()));
+        assert_eq!(bundle.insert_double_vec("double", &[123.4]), Ok(()));
+        assert_eq!(bundle.insert_string_vec("string", &["foo", "bar", "baz"]), Ok(()));
+        assert_eq!(
+            bundle.insert_string_vec(
+                "string",
+                &[&"foo".to_string(), &"bar".to_string(), &"baz".to_string()]
+            ),
+            Ok(())
+        );
+        assert_eq!(
+            bundle.insert_string_vec(
+                "string",
+                &["foo".to_string(), "bar".to_string(), "baz".to_string()]
+            ),
+            Ok(())
+        );
+
+        assert_eq!(bundle.size(), 5);
+
+        assert_eq!(bundle.get_bool_vec("bool"), Ok(Some(vec![])));
+        assert_eq!(bundle.get_int_vec("int"), Ok(Some(vec![42])));
+        assert_eq!(bundle.get_long_vec("long"), Ok(Some(vec![66, 67, 68])));
+        assert_eq!(bundle.get_double_vec("double"), Ok(Some(vec![123.4])));
+    }
+
+    #[test]
+    fn insert_get_bundle() {
+        let mut bundle = PersistableBundle::new();
+
+        let mut sub_bundle = PersistableBundle::new();
+        assert_eq!(sub_bundle.insert_int("int", 42), Ok(()));
+        assert_eq!(sub_bundle.size(), 1);
+        assert_eq!(bundle.insert_persistable_bundle("bundle", &sub_bundle), Ok(()));
+
+        assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle)));
+    }
 }
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index deeaa37..c19e375 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -92,6 +92,11 @@
 #endif
 };
 
+enum {
+    APERSISTABLEBUNDLE_KEY_NOT_FOUND = APERSISTABLEBUNDLE_KEY_NOT_FOUND,
+    APERSISTABLEBUNDLE_ALLOCATOR_FAILED = APERSISTABLEBUNDLE_ALLOCATOR_FAILED,
+};
+
 } // namespace consts
 
 } // namespace c_interface
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index c74ba0a..65ad896 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -47,6 +47,71 @@
         return mHaveMessage ? OK : WOULD_BLOCK;
     }
 
+    void moveMsgStart(ipc_msg_t* msg, size_t msg_size, size_t offset) {
+        LOG_ALWAYS_FATAL_IF(offset > msg_size, "tried to move message past its end %zd>%zd", offset,
+                            msg_size);
+        while (true) {
+            if (offset == 0) {
+                break;
+            }
+            if (offset >= msg->iov[0].iov_len) {
+                // Move to the next iov, this one was sent already
+                offset -= msg->iov[0].iov_len;
+                msg->iov++;
+                msg->num_iov -= 1;
+            } else {
+                // We need to move the base of the current iov
+                msg->iov[0].iov_len -= offset;
+                msg->iov[0].iov_base = static_cast<char*>(msg->iov[0].iov_base) + offset;
+                offset = 0;
+            }
+        }
+        // We only send handles on the first message. This can be changed in the future if we want
+        // to send more handles than the maximum per message limit (which would require sending
+        // multiple messages). The current code makes sure that we send less handles than the
+        // maximum trusty allows.
+        msg->num_handles = 0;
+    }
+
+    status_t sendTrustyMsg(ipc_msg_t* msg, size_t msg_size) {
+        do {
+            ssize_t rc = send_msg(mSocket.fd.get(), msg);
+            if (rc == ERR_NOT_ENOUGH_BUFFER) {
+                // Peer is blocked, wait until it unblocks.
+                // TODO: when tipc supports a send-unblocked handler,
+                // save the message here in a queue and retry it asynchronously
+                // when the handler gets called by the library
+                uevent uevt;
+                do {
+                    rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
+                    if (rc < 0) {
+                        return statusFromTrusty(rc);
+                    }
+                    if (uevt.event & IPC_HANDLE_POLL_HUP) {
+                        return DEAD_OBJECT;
+                    }
+                } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
+
+                // Retry the send, it should go through this time because
+                // sending is now unblocked
+                rc = send_msg(mSocket.fd.get(), msg);
+            }
+            if (rc < 0) {
+                return statusFromTrusty(rc);
+            }
+            size_t sent_bytes = static_cast<size_t>(rc);
+            if (sent_bytes < msg_size) {
+                moveMsgStart(msg, msg_size, static_cast<size_t>(sent_bytes));
+                msg_size -= sent_bytes;
+            } else {
+                LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != msg_size,
+                                    "Sent the wrong number of bytes %zd!=%zu", rc, msg_size);
+                break;
+            }
+        } while (true);
+        return OK;
+    }
+
     status_t interruptableWriteFully(
             FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
             const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
@@ -86,34 +151,7 @@
             msg.handles = msgHandles;
         }
 
-        ssize_t rc = send_msg(mSocket.fd.get(), &msg);
-        if (rc == ERR_NOT_ENOUGH_BUFFER) {
-            // Peer is blocked, wait until it unblocks.
-            // TODO: when tipc supports a send-unblocked handler,
-            // save the message here in a queue and retry it asynchronously
-            // when the handler gets called by the library
-            uevent uevt;
-            do {
-                rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
-                if (rc < 0) {
-                    return statusFromTrusty(rc);
-                }
-                if (uevt.event & IPC_HANDLE_POLL_HUP) {
-                    return DEAD_OBJECT;
-                }
-            } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
-
-            // Retry the send, it should go through this time because
-            // sending is now unblocked
-            rc = send_msg(mSocket.fd.get(), &msg);
-        }
-        if (rc < 0) {
-            return statusFromTrusty(rc);
-        }
-        LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size,
-                            "Sent the wrong number of bytes %zd!=%zu", rc, size);
-
-        return OK;
+        return sendTrustyMsg(&msg, size);
     }
 
     status_t interruptableReadFully(
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 06f00a4..8062a2e 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -85,6 +85,7 @@
 // We use the top 10 layers as a way to haphazardly place ourselves above anything else.
 static const int LAYER_BASE = INT32_MAX - 10;
 static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
 
 class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
 public:
@@ -203,8 +204,8 @@
         ASSERT_EQ(InputEventType::MOTION, ev->getType());
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
-        EXPECT_EQ(x, mev->getX(0));
-        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_NEAR(x, mev->getX(0), EPSILON);
+        EXPECT_NEAR(y, mev->getY(0), EPSILON);
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index bf928f4..f17575d 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -244,3 +244,11 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "deprecate_uiautomation_input_injection"
+  namespace: "input"
+  description: "Deprecate UiAutomation#injectInputEvent and UiAutomation#injectInputEventToInputFilter test/hidden APIs"
+  bug: "277261245"
+}
+
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index ea5605d..67f4aa1 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -23,6 +23,7 @@
 #include <future>
 
 #include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <private/gui/SyncFeatures.h>
 #include <processgroup/processgroup.h>
@@ -60,7 +61,7 @@
 
     struct sched_param param = {0};
     int sched_policy;
-    if (enabled) {
+    if (enabled && !FlagManager::getInstance().disable_sched_fifo_re()) {
         sched_policy = SCHED_FIFO;
         param.sched_priority = kFifoPriority;
     } else {
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index 7347ac7..4929b3f 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1 +1 @@
-bduddie@google.com
+include platform/frameworks/native:/services/sensorservice/OWNERS
\ No newline at end of file
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 99360b9..8db9a78 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,11 +35,11 @@
     {
     }
 
-    // This should never fail
     virtual sp<media::IAudioManagerNative> getNativeInterface() {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
         const status_t res = remote()->transact(GET_NATIVE_INTERFACE, data, &reply, 0);
+        if (res == DEAD_OBJECT) return nullptr;
         LOG_ALWAYS_FATAL_IF(res != OK, "%s failed with result %d", __func__, res);
         const int ex = reply.readExceptionCode();
         LOG_ALWAYS_FATAL_IF(ex != binder::Status::EX_NONE, "%s failed with exception %d",
@@ -48,7 +48,7 @@
         sp<IBinder> binder;
         const status_t err = reply.readNullableStrongBinder(&binder);
         LOG_ALWAYS_FATAL_IF(binder == nullptr, "%s failed unexpected nullptr %d", __func__, err);
-        const auto iface = checked_interface_cast<media::IAudioManagerNative>(std::move(binder));
+        const auto iface = checked_interface_cast<media::IAudioManagerNative>(binder);
         LOG_ALWAYS_FATAL_IF(iface == nullptr, "%s failed unexpected interface", __func__);
         return iface;
     }
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index cc04684..535c7ae 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -16,7 +16,9 @@
 
 #include "AndroidInputEventProtoConverter.h"
 
+#include <android/input.h>
 #include <android-base/logging.h>
+#include <input/Input.h>
 #include <perfetto/trace/android/android_input_event.pbzero.h>
 
 namespace android::inputdispatcher::trace {
@@ -51,11 +53,15 @@
     outProto.set_classification(static_cast<int32_t>(event.classification));
     outProto.set_flags(event.flags);
     outProto.set_policy_flags(event.policyFlags);
+    outProto.set_button_state(event.buttonState);
+    outProto.set_action_button(event.actionButton);
 
     if (!isRedacted) {
         outProto.set_cursor_position_x(event.xCursorPosition);
         outProto.set_cursor_position_y(event.yCursorPosition);
         outProto.set_meta_state(event.metaState);
+        outProto.set_precision_x(event.xPrecision);
+        outProto.set_precision_y(event.yPrecision);
     }
 
     for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
@@ -67,6 +73,12 @@
 
         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.
+            bits.markBit(AMOTION_EVENT_AXIS_X);
+            bits.markBit(AMOTION_EVENT_AXIS_Y);
+        }
         for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
             const auto axis = bits.clearFirstMarkedBit();
             auto axisEntry = pointer->add_axis_value();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b6e27a8..6b4c4b7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -14914,4 +14914,195 @@
     mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithFlags(0)));
 }
 
+class TransferOrDontTransferFixture : public InputDispatcherTest,
+                                      public ::testing::WithParamInterface<bool> {
+public:
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+        mFromWindow =
+                sp<FakeWindowHandle>::make(app, mDispatcher, "From", ui::LogicalDisplayId::DEFAULT);
+        mToWindow =
+                sp<FakeWindowHandle>::make(app, mDispatcher, "To", ui::LogicalDisplayId::DEFAULT);
+
+        mDispatcher->onWindowInfosChanged(
+                {{*mFromWindow->getInfo(), *mToWindow->getInfo()}, {}, 0, 0});
+    }
+
+protected:
+    sp<FakeWindowHandle> mFromWindow;
+    sp<FakeWindowHandle> mToWindow;
+};
+
+// Start a touch gesture and then continue hovering the mouse at the same time.
+// After the mouse is hovering, invoke transferTouch API. Check the events that
+// are received by each of the windows.
+TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) {
+    SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+
+    const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    const int32_t mouseDeviceId = 6;
+    const int32_t touchDeviceId = 4;
+
+    // Send touch down to the first window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .downTime(baseTime + 10)
+                                      .eventTime(baseTime + 10)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .build());
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+    // Send touch move to the first window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .downTime(baseTime + 10)
+                                      .eventTime(baseTime + 20)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100))
+                                      .build());
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+    // Start mouse hover on the first window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .downTime(baseTime + 30)
+                                      .eventTime(baseTime + 30)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200))
+                                      .build());
+
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    if (GetParam()) {
+        // Call transferTouchGesture
+        const bool transferred =
+                mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken());
+        ASSERT_TRUE(transferred);
+
+        mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+        // b/382473355: For some reason, mToWindow also receives HOVER_EXIT first
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+        // Further touch events should be delivered to mTowindow (?)
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 40)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 50)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        // b/382473355: Even though the window got ACTION_DOWN, it's no longer receiving the
+        // remainder of the touch gesture.
+
+        mFromWindow->assertNoEvents();
+        mToWindow->assertNoEvents();
+    } else {
+        // Don't call transferTouchGesture
+
+        // Further touch events should be dropped
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 40)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        mFromWindow->assertNoEvents();
+        mToWindow->assertNoEvents();
+    }
+}
+
+TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevice) {
+    SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+
+    const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    const int32_t mouseDeviceId = 6;
+    const int32_t touchDeviceId = 4;
+
+    // Send touch down to the 'From' window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .downTime(baseTime + 10)
+                                      .eventTime(baseTime + 10)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+                                      .build());
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+    // Send touch move to the 'From' window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(touchDeviceId)
+                                      .downTime(baseTime + 10)
+                                      .eventTime(baseTime + 20)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100))
+                                      .build());
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+    // Start mouse hover on the 'From' window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .deviceId(mouseDeviceId)
+                                      .downTime(baseTime + 30)
+                                      .eventTime(baseTime + 30)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200))
+                                      .build());
+
+    mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    if (GetParam()) {
+        // Call transferTouchGesture
+        const bool transferred =
+                mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken());
+        ASSERT_TRUE(transferred);
+        mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+        mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+        // Further touch events should be delivered to mToWindow
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 40)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 50)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        // b/382473355: Even though the window got ACTION_DOWN, it's receiving another DOWN!
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+        mToWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+        mFromWindow->assertNoEvents();
+        mToWindow->assertNoEvents();
+    } else {
+        // Don't call transferTouchGesture
+
+        mDispatcher->notifyMotion(
+                MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                        .deviceId(touchDeviceId)
+                        .downTime(baseTime + 10)
+                        .eventTime(baseTime + 40)
+                        .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+                        .build());
+        mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+        mFromWindow->assertNoEvents();
+        mToWindow->assertNoEvents();
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool());
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 470e65b..50cbedf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7364,35 +7364,39 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 
     // down when BTN_TOUCH is pressed, pressure defaults to 1
+    processPosition(mapper, 151, 251);
     processKey(mapper, BTN_TOUCH, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
+    // down at the new position
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // up when BTN_TOUCH is released, hover restored
+    processPosition(mapper, 152, 252);
     processKey(mapper, BTN_TOUCH, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    // UP should have the same coordinates as the previous event
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
+            toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
+    // HOVER_ENTER at the new position
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 
     // exit hover when pointer goes away
     processId(mapper, -1);
@@ -7400,7 +7404,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
@@ -7435,35 +7439,39 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 
     // down when pressure becomes non-zero
+    processPosition(mapper, 151, 251);
     processPressure(mapper, RAW_PRESSURE_MAX);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
+    // down at the new position
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // up when pressure becomes 0, hover restored
+    processPosition(mapper, 152, 252);
     processPressure(mapper, 0);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    // UP should have the same coordinates as the previous event
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
+            toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
+    // HOVER_ENTER at the new position
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 
     // exit hover when pointer goes away
     processId(mapper, -1);
@@ -7471,7 +7479,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+            toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
 /**
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index 7347ac7..3ccdc17 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1 +1,3 @@
 bduddie@google.com
+arthuri@google.com
+rockyfang@google.com
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 5cef051..bb6bebe 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -357,7 +357,9 @@
     mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
 
     ndk::SpAIBinder binder = mAidlComposerCallback->asBinder();
-    AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);
+    if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) {
+        AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);
+    }
 
     const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
     if (!status.isOk()) {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index fc317f3..a010353 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -28,6 +28,7 @@
 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <android/binder_manager.h>
 #include <android/hardware/graphics/composer/2.1/types.h>
+#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <hidl/HidlTransportSupport.h>
@@ -301,7 +302,9 @@
 }
 
 void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
-    android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+    if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) {
+        android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+    }
 
     auto ret = [&]() {
         if (mClient_2_4) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 7289e2f..9119247 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -264,28 +264,21 @@
     }
     snapshot.isVisible = visible;
 
-    if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
-        const bool visibleForInput =
-                snapshot.isVisible || (snapshot.hasInputInfo() && !snapshot.isHiddenByPolicy());
-        snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
-                                          !visibleForInput);
-    } else {
-        // TODO(b/238781169) we are ignoring this compat for now, since we will have
-        // to remove any optimization based on visibility.
+    // TODO(b/238781169) we are ignoring this compat for now, since we will have
+    // to remove any optimization based on visibility.
 
-        // For compatibility reasons we let layers which can receive input
-        // receive input before they have actually submitted a buffer. Because
-        // of this we use canReceiveInput instead of isVisible to check the
-        // policy-visibility, ignoring the buffer state. However for layers with
-        // hasInputInfo()==false we can use the real visibility state.
-        // We are just using these layers for occlusion detection in
-        // InputDispatcher, and obviously if they aren't visible they can't occlude
-        // anything.
-        const bool visibleForInput =
-                snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
-        snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
-                                          !visibleForInput);
-    }
+    // For compatibility reasons we let layers which can receive input
+    // receive input before they have actually submitted a buffer. Because
+    // of this we use canReceiveInput instead of isVisible to check the
+    // policy-visibility, ignoring the buffer state. However for layers with
+    // hasInputInfo()==false we can use the real visibility state.
+    // We are just using these layers for occlusion detection in
+    // InputDispatcher, and obviously if they aren't visible they can't occlude
+    // anything.
+    const bool visibleForInput =
+            snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
+    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+
     LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
           snapshot.getDebugString().c_str());
 }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index c6d7160..0efc396 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -344,7 +344,8 @@
     auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
                                                       IPCThreadState::self()->getCallingUid(),
                                                       eventRegistration);
-    if (FlagManager::getInstance().misc1()) {
+    if (FlagManager::getInstance().misc1() &&
+        !FlagManager::getInstance().disable_sched_fifo_sf_sched()) {
         const int policy = SCHED_FIFO;
         connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
     }
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index 20c58eb..6a5eeba 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -24,6 +24,7 @@
 #include <sys/timerfd.h>
 #include <sys/unistd.h>
 
+#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <ftl/concat.h>
 #include <ftl/enum.h>
@@ -155,8 +156,10 @@
     setDebugState(DebugState::Running);
     struct sched_param param = {0};
     param.sched_priority = 2;
-    if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
-        ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+    if (!FlagManager::getInstance().disable_sched_fifo_sf_sched()) {
+        if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
+            ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+        }
     }
 
     if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d6225e2..1038b9f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7116,7 +7116,7 @@
 
     struct sched_param param = {0};
     int sched_policy;
-    if (enabled) {
+    if (enabled && !FlagManager::getInstance().disable_sched_fifo_sf()) {
         sched_policy = SCHED_FIFO;
         param.sched_priority = kFifoPriority;
     } else {
@@ -7907,9 +7907,8 @@
     const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
-    if (const bool isPacesetter =
-                mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
-                                                 /*clearContentRequirements*/ true)) {
+    if (mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
+                                         /*clearContentRequirements*/ true)) {
         mDisplayModeController.updateKernelIdleTimer(displayId);
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a793d50..158bf57 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -70,7 +70,6 @@
 
 #include <common/FlagManager.h>
 #include "ActivePictureTracker.h"
-#include "BackgroundExecutor.h"
 #include "Display/DisplayModeController.h"
 #include "Display/PhysicalDisplay.h"
 #include "Display/VirtualDisplaySnapshot.h"
@@ -102,12 +101,9 @@
 #include <atomic>
 #include <cstdint>
 #include <functional>
-#include <map>
 #include <memory>
 #include <mutex>
 #include <optional>
-#include <queue>
-#include <set>
 #include <string>
 #include <thread>
 #include <type_traits>
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index e80cd78..15df152 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -104,9 +104,19 @@
     dumpFlag(result, (aconfig), #name, std::bind(&FlagManager::name, this))
 #define DUMP_LEGACY_SERVER_FLAG(name) DUMP_FLAG_INTERNAL(name, false)
 #define DUMP_ACONFIG_FLAG(name) DUMP_FLAG_INTERNAL(name, true)
+#define DUMP_SYSPROP_FLAG(name) \
+    dumpFlag(result, (true), "debug.sf." #name, std::bind(&FlagManager::name, this))
 
     base::StringAppendF(&result, "FlagManager values: \n");
 
+    /// Sysprop flags ///
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_sf);
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_binder);
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_sched);
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_re);
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_composer);
+    DUMP_SYSPROP_FLAG(disable_sched_fifo_composer_callback);
+
     /// Legacy server flags ///
     DUMP_LEGACY_SERVER_FLAG(use_adpf_cpu_hint);
     DUMP_LEGACY_SERVER_FLAG(use_skia_tracing);
@@ -185,6 +195,12 @@
     const auto res = parseBool(value.c_str());
     return res.has_value() && res.value();
 }
+#define FLAG_MANAGER_SYSPROP_FLAG(name, defaultVal)                                      \
+    bool FlagManager::name() const {                                                     \
+        static const bool kFlagValue =                                                   \
+                base::GetBoolProperty("debug.sf." #name, /* default value*/ defaultVal); \
+        return kFlagValue;                                                               \
+    }
 
 #define FLAG_MANAGER_LEGACY_SERVER_FLAG(name, syspropOverride, serverFlagName)              \
     bool FlagManager::name() const {                                                        \
@@ -215,6 +231,14 @@
 #define FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(name, syspropOverride, owner) \
     FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner)
 
+/// Debug sysprop flags - default value is always false ///
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_binder, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_sched, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_re, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer_callback, /* default */ false)
+
 /// Legacy server flags ///
 FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
 FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint",
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index c7f97b4..147e79e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -42,6 +42,14 @@
 
     void setUnitTestMode();
 
+    /// Debug sysprop flags ///
+    bool disable_sched_fifo_sf() const;
+    bool disable_sched_fifo_sf_binder() const;
+    bool disable_sched_fifo_sf_sched() const;
+    bool disable_sched_fifo_re() const;
+    bool disable_sched_fifo_composer() const;
+    bool disable_sched_fifo_composer_callback() const;
+
     /// Legacy server flags ///
     bool test_flag() const;
     bool use_adpf_cpu_hint() const;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 6c8972f..73dfa9f 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -132,7 +132,8 @@
     // Set the minimum policy of surfaceflinger node to be SCHED_FIFO.
     // So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run
     // at least with SCHED_FIFO policy and priority 1.
-    if (errorInPriorityModification == 0) {
+    if (errorInPriorityModification == 0 &&
+        !FlagManager::getInstance().disable_sched_fifo_sf_binder()) {
         flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
     }
 
@@ -150,7 +151,8 @@
 
     // publish gui::ISurfaceComposer, the new AIDL interface
     sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
-    if (FlagManager::getInstance().misc1()) {
+    if (FlagManager::getInstance().misc1() &&
+        !FlagManager::getInstance().disable_sched_fifo_composer()) {
         composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
     }
     sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 3ddc4f2..536a6b3 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -131,9 +131,10 @@
     return HalResult<void>::unsupported();
 }
 
-HalResult<void> HalWrapper::composePwleV2(const CompositePwleV2&, const std::function<void()>&) {
+HalResult<milliseconds> HalWrapper::composePwleV2(const CompositePwleV2&,
+                                                  const std::function<void()>&) {
     ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL");
-    return HalResult<void>::unsupported();
+    return HalResult<milliseconds>::unsupported();
 }
 
 HalResult<Capabilities> HalWrapper::getCapabilities() {
@@ -359,11 +360,18 @@
     return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb));
 }
 
-HalResult<void> AidlHalWrapper::composePwleV2(const CompositePwleV2& composite,
-                                              const std::function<void()>& completionCallback) {
+HalResult<milliseconds> AidlHalWrapper::composePwleV2(
+        const CompositePwleV2& composite, const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
-    return HalResultFactory::fromStatus(getHal()->composePwleV2(composite, cb));
+
+    milliseconds totalDuration(0);
+    for (const auto& primitive : composite.pwlePrimitives) {
+        totalDuration += milliseconds(primitive.timeMillis);
+    }
+
+    return HalResultFactory::fromStatus<milliseconds>(getHal()->composePwleV2(composite, cb),
+                                                      totalDuration);
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 339a6e1..9a39ad4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -423,8 +423,8 @@
     virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives,
                                               const std::function<void()>& completionCallback);
 
-    virtual HalResult<void> composePwleV2(const CompositePwleV2& composite,
-                                          const std::function<void()>& completionCallback);
+    virtual HalResult<std::chrono::milliseconds> composePwleV2(
+            const CompositePwleV2& composite, const std::function<void()>& completionCallback);
 
 protected:
     // Shared pointer to allow CallbackScheduler to outlive this wrapper.
@@ -511,8 +511,9 @@
             const std::vector<PrimitivePwle>& primitives,
             const std::function<void()>& completionCallback) override final;
 
-    HalResult<void> composePwleV2(const CompositePwleV2& composite,
-                                  const std::function<void()>& completionCallback) override final;
+    HalResult<std::chrono::milliseconds> composePwleV2(
+            const CompositePwleV2& composite,
+            const std::function<void()>& completionCallback) override final;
 
 protected:
     HalResult<Capabilities> getCapabilitiesInternal() override final;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index c58e05c..7545148 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -787,5 +787,6 @@
 
     result = mWrapper->composePwleV2(composite, callback);
     ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(300ms, result.value());
     ASSERT_EQ(1, *callbackCounter.get());
 }