Merge "Extract SensorInputMapperTest to its own file" into main
diff --git a/data/etc/android.hardware.xr.input.controller.xml b/data/etc/android.hardware.xr.input.controller.xml
new file mode 100644
index 0000000..1fb8b41
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.controller.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+     input from XR controllers. -->
+<permissions>
+    <feature name="android.hardware.xr.input.controller" />
+</permissions>
diff --git a/data/etc/android.hardware.xr.input.eye_tracking.xml b/data/etc/android.hardware.xr.input.eye_tracking.xml
new file mode 100644
index 0000000..8c6c2ed
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.eye_tracking.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+     input from an XR user's eyes. -->
+<permissions>
+    <feature name="android.hardware.xr.input.eye_tracking" />
+</permissions>
diff --git a/data/etc/android.hardware.xr.input.hand_tracking.xml b/data/etc/android.hardware.xr.input.hand_tracking.xml
new file mode 100644
index 0000000..6de3bee
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.hand_tracking.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+     input from an XR user's hands. -->
+<permissions>
+    <feature name="android.hardware.xr.input.hand_tracking" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_0.xml b/data/etc/android.software.xr.api.openxr-1_0.xml
new file mode 100644
index 0000000..71c4a94
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+     that supports OpenXR 1.0 (0x00010000). -->
+<permissions>
+    <feature name="android.software.xr.api.openxr" version="65536" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_1.xml b/data/etc/android.software.xr.api.openxr-1_1.xml
new file mode 100644
index 0000000..45c1065
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+     that supports OpenXR 1.1 (0x00010001). -->
+<permissions>
+    <feature name="android.software.xr.api.openxr" version="65537" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_2.xml b/data/etc/android.software.xr.api.openxr-1_2.xml
new file mode 100644
index 0000000..ba11b8d
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+     that supports OpenXR 1.2 (0x00010002). -->
+<permissions>
+    <feature name="android.software.xr.api.openxr" version="65538" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.spatial-1.xml b/data/etc/android.software.xr.api.spatial-1.xml
new file mode 100644
index 0000000..ce425aa
--- /dev/null
+++ b/data/etc/android.software.xr.api.spatial-1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+     that supports Android XR Spatial APIs, version 1. -->
+<permissions>
+    <feature name="android.software.xr.api.spatial" version="1" />
+</permissions>
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 228347d..2e346bb 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -211,16 +211,17 @@
      * `consumeBatchedInputEvents`.
      */
     std::map<DeviceId, std::queue<InputMessage>> mBatches;
+
     /**
-     * Creates a MotionEvent by consuming samples from the provided queue. If one message has
-     * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is
-     * assumed that messages are queued in chronological order. In other words, only events that
-     * occurred prior to the adjustedFrameTime will be consumed.
-     * @param requestedFrameTime the time up to which to consume events.
-     * @param messages the queue of messages to consume from
+     * Creates a MotionEvent by consuming samples from the provided queue. Consumes all messages
+     * with eventTime <= requestedFrameTime - resampleLatency, where `resampleLatency` is latency
+     * introduced by the resampler. Assumes that messages are queued in chronological order.
+     * @param requestedFrameTime The time up to which consume messages, as given by the inequality
+     * above. If std::nullopt, everything in messages will be consumed.
+     * @param messages the queue of messages to consume from.
      */
     std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent(
-            const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages);
+            const std::optional<nsecs_t> requestedFrameTime, std::queue<InputMessage>& messages);
 
     /**
      * Consumes the batched input events, optionally allowing the caller to specify a device id
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index f7b9f05..d32eecd 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -105,7 +105,8 @@
 };
 
 bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
-    if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) {
+    sp<ProcessState> self = ProcessState::selfOrNull();
+    if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
         ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
               "implemented. serviceName: %s",
               serviceName.c_str());
@@ -172,10 +173,6 @@
     mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>();
 }
 
-sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
-    return mTheRealServiceManager;
-}
-
 Status BackendUnifiedServiceManager::getService(const ::std::string& name,
                                                 sp<IBinder>* _aidl_return) {
     os::Service service;
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index feb8470..abc0eda 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -121,7 +121,6 @@
 public:
     explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
 
-    sp<os::IServiceManager> getImpl();
     binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
     binder::Status getService2(const ::std::string& name, os::Service* out) override;
     binder::Status checkService(const ::std::string& name, os::Service* out) override;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 32388db..39d8c24 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -561,8 +561,9 @@
     sp<IBinder> svc = checkService(name);
     if (svc != nullptr) return svc;
 
+    sp<ProcessState> self = ProcessState::selfOrNull();
     const bool isVendorService =
-        strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
+            self && strcmp(self->getDriverName().c_str(), "/dev/vndbinder") == 0;
     constexpr auto timeout = 5s;
     const auto startTime = std::chrono::steady_clock::now();
     // Vendor code can't access system properties
@@ -579,7 +580,7 @@
     const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
 
     ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(),
-          ProcessState::self()->getDriverName().c_str());
+          self ? self->getDriverName().c_str() : "RPC accessors only");
 
     int n = 0;
     while (std::chrono::steady_clock::now() - startTime < timeout) {
@@ -661,7 +662,8 @@
     if (Status status = realGetService(name, &out); !status.isOk()) {
         ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
               status.toString8().c_str());
-        if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) {
+        sp<ProcessState> self = ProcessState::selfOrNull();
+        if (self && 0 == self->getThreadPoolMaxTotalThreadCount()) {
             ALOGW("Got service, but may be racey because we could not wait efficiently for it. "
                   "Threadpool has 0 guaranteed threads. "
                   "Is the threadpool configured properly? "
@@ -695,9 +697,10 @@
             if (waiter->mBinder != nullptr) return waiter->mBinder;
         }
 
+        sp<ProcessState> self = ProcessState::selfOrNull();
         ALOGW("Waited one second for %s (is service started? Number of threads started in the "
               "threadpool: %zu. Are binder threads started and available?)",
-              name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount());
+              name.c_str(), self ? self->getThreadPoolMaxTotalThreadCount() : 0);
 
         // Handle race condition for lazy services. Here is what can happen:
         // - the service dies (not processed by init yet).
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 020ebcc..8404a48 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -139,6 +139,9 @@
         "--raw-line",
         "use libc::sockaddr;",
     ],
+    cflags: [
+        "-DANDROID_PLATFORM",
+    ],
     shared_libs: [
         "libbinder_ndk",
     ],
@@ -179,6 +182,9 @@
         // rustified
         "libbinder_ndk_bindgen_flags.txt",
     ],
+    cflags: [
+        "-DANDROID_PLATFORM",
+    ],
     shared_libs: [
         "libbinder_ndk_on_trusty_mock",
         "libc++",
diff --git a/libs/binder/rust/Cargo.toml b/libs/binder/rust/Cargo.toml
new file mode 100644
index 0000000..e5738c5
--- /dev/null
+++ b/libs/binder/rust/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "android-binder"
+version = "0.1.0"
+edition = "2021"
+description = "Safe bindings to Android Binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+binder-ndk-sys = { package = "android-binder-ndk-sys", version = "0.1", path = "./sys" }
+downcast-rs = "1.2.1"
+libc = "0.2.159"
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ["cfg(android_vendor)", "cfg(android_ndk)", "cfg(android_vndk)", "cfg(trusty)"]
diff --git a/libs/binder/rust/build.rs b/libs/binder/rust/build.rs
new file mode 100644
index 0000000..f3e6b53
--- /dev/null
+++ b/libs/binder/rust/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+    // Anything with cargo is NDK only. If you want to access anything else, use Soong.
+    println!("cargo::rustc-cfg=android_ndk");
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 23026e5..8c0501b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -207,8 +207,10 @@
 /// Corresponds to TF_ONE_WAY -- an asynchronous call.
 pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
 /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+#[cfg(not(android_ndk))]
 pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
 /// Set to the vendor flag if we are building for the VNDK, 0 otherwise
+#[cfg(not(android_ndk))]
 pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL;
 
 /// Internal interface of binder local or remote objects for making
@@ -221,7 +223,7 @@
     fn is_binder_alive(&self) -> bool;
 
     /// Indicate that the service intends to receive caller security contexts.
-    #[cfg(not(android_vndk))]
+    #[cfg(not(any(android_vndk, android_ndk)))]
     fn set_requesting_sid(&mut self, enable: bool);
 
     /// Dump this object to the given file handle
@@ -346,7 +348,6 @@
                 panic!("Expected non-null class pointer from AIBinder_Class_define!");
             }
             sys::AIBinder_Class_setOnDump(class, Some(I::on_dump));
-            sys::AIBinder_Class_setHandleShellCommand(class, None);
             class
         };
         InterfaceClass(ptr)
@@ -714,7 +715,7 @@
 pub struct BinderFeatures {
     /// Indicates that the service intends to receive caller security contexts. This must be true
     /// for `ThreadState::with_calling_sid` to work.
-    #[cfg(not(android_vndk))]
+    #[cfg(not(any(android_vndk, android_ndk)))]
     pub set_requesting_sid: bool,
     // Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
     // when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
@@ -916,8 +917,12 @@
         impl $native {
             /// Create a new binder service.
             pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
+                #[cfg(not(android_ndk))]
                 let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
-                #[cfg(not(android_vndk))]
+                #[cfg(android_ndk)]
+                let mut binder = $crate::binder_impl::Binder::new($native(Box::new(inner)));
+
+                #[cfg(not(any(android_vndk, android_ndk)))]
                 $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index f7f3f35..14493db 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -100,11 +100,11 @@
 mod native;
 mod parcel;
 mod proxy;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
 mod service;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
 mod state;
-#[cfg(not(any(android_vendor, android_vndk)))]
+#[cfg(not(any(android_vendor, android_ndk, android_vndk)))]
 mod system_only;
 
 use binder_ndk_sys as sys;
@@ -114,15 +114,18 @@
 pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
 pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
 pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
 pub use service::{
     add_service, check_interface, check_service, force_lazy_services_persist,
-    get_declared_instances, get_interface, get_service, is_declared, is_handling_transaction,
-    register_lazy_service, wait_for_interface, wait_for_service, LazyServiceGuard,
+    get_declared_instances, is_declared, is_handling_transaction, register_lazy_service,
+    wait_for_interface, wait_for_service, LazyServiceGuard,
 };
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
+#[allow(deprecated)]
+pub use service::{get_interface, get_service};
+#[cfg(not(any(trusty, android_ndk)))]
 pub use state::{ProcessState, ThreadState};
-#[cfg(not(any(android_vendor, android_vndk)))]
+#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
 pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
 
 /// Binder result containing a [`Status`] on error.
@@ -134,9 +137,10 @@
     pub use crate::binder::{
         IBinderInternal, InterfaceClass, LocalStabilityType, Remotable, Stability, StabilityType,
         ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, VintfStabilityType,
-        FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
-        LAST_CALL_TRANSACTION,
+        FIRST_CALL_TRANSACTION, FLAG_ONEWAY, LAST_CALL_TRANSACTION,
     };
+    #[cfg(not(android_ndk))]
+    pub use crate::binder::{FLAG_CLEAR_BUF, FLAG_PRIVATE_LOCAL};
     pub use crate::binder_async::BinderAsyncRuntime;
     pub use crate::error::status_t;
     pub use crate::native::Binder;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index c87cc94..9e1cfd6 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-use crate::binder::{
-    AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
-};
+#[cfg(not(android_ndk))]
+use crate::binder::Stability;
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
 use crate::error::{status_result, status_t, Result, StatusCode};
 use crate::parcel::{BorrowedParcel, Serialize};
 use crate::proxy::SpIBinder;
@@ -76,14 +76,32 @@
     /// This moves the `rust_object` into an owned [`Box`] and Binder will
     /// manage its lifetime.
     pub fn new(rust_object: T) -> Binder<T> {
-        Self::new_with_stability(rust_object, Stability::default())
+        #[cfg(not(android_ndk))]
+        {
+            Self::new_with_stability(rust_object, Stability::default())
+        }
+        #[cfg(android_ndk)]
+        {
+            Self::new_unmarked(rust_object)
+        }
     }
 
     /// Create a new Binder remotable object with the given stability
     ///
     /// This moves the `rust_object` into an owned [`Box`] and Binder will
     /// manage its lifetime.
+    #[cfg(not(android_ndk))]
     pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
+        let mut binder = Self::new_unmarked(rust_object);
+        binder.mark_stability(stability);
+        binder
+    }
+
+    /// Creates a new Binder remotable object with unset stability
+    ///
+    /// This is internal because normally we want to set the stability explicitly,
+    /// however for the NDK variant we cannot mark the stability.
+    fn new_unmarked(rust_object: T) -> Binder<T> {
         let class = T::get_class();
         let rust_object = Box::into_raw(Box::new(rust_object));
         // Safety: `AIBinder_new` expects a valid class pointer (which we
@@ -93,9 +111,7 @@
         // decremented via `AIBinder_decStrong` when the reference lifetime
         // ends.
         let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
-        let mut binder = Binder { ibinder, rust_object };
-        binder.mark_stability(stability);
-        binder
+        Binder { ibinder, rust_object }
     }
 
     /// Set the extension of a binder interface. This allows a downstream
@@ -189,6 +205,7 @@
     }
 
     /// Mark this binder object with the given stability guarantee
+    #[cfg(not(android_ndk))]
     fn mark_stability(&mut self, stability: Stability) {
         match stability {
             Stability::Local => self.mark_local_stability(),
@@ -215,7 +232,7 @@
 
     /// Mark this binder object with local stability, which is vendor if we are
     /// building for android_vendor and system otherwise.
-    #[cfg(not(android_vendor))]
+    #[cfg(not(any(android_vendor, android_ndk)))]
     fn mark_local_stability(&mut self) {
         // Safety: Self always contains a valid `AIBinder` pointer, so we can
         // always call this C API safely.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 3bfc425..485b0bd 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -197,6 +197,7 @@
 // Data serialization methods
 impl<'a> BorrowedParcel<'a> {
     /// Data written to parcelable is zero'd before being deleted or reallocated.
+    #[cfg(not(android_ndk))]
     pub fn mark_sensitive(&mut self) {
         // Safety: guaranteed to have a parcel object, and this method never fails
         unsafe { sys::AParcel_markSensitive(self.as_native()) }
@@ -342,6 +343,7 @@
 
 impl Parcel {
     /// Data written to parcelable is zero'd before being deleted or reallocated.
+    #[cfg(not(android_ndk))]
     pub fn mark_sensitive(&mut self) {
         self.borrowed().mark_sensitive()
     }
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 04f1517..593d12c 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -298,7 +298,7 @@
         unsafe { sys::AIBinder_isAlive(self.as_native()) }
     }
 
-    #[cfg(not(android_vndk))]
+    #[cfg(not(any(android_vndk, android_ndk)))]
     fn set_requesting_sid(&mut self, enable: bool) {
         // Safety: `SpIBinder` guarantees that `self` always contains a valid
         // pointer to an `AIBinder`.
diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs
index 29dd8e1..f4fdcf5 100644
--- a/libs/binder/rust/src/service.rs
+++ b/libs/binder/rust/src/service.rs
@@ -176,6 +176,7 @@
 /// seconds if it doesn't yet exist.
 #[deprecated = "this polls 5s, use wait_for_interface or check_interface"]
 pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+    #[allow(deprecated)]
     interface_cast(get_service(name))
 }
 
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index bd666fe..557f0e8 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -15,15 +15,19 @@
  */
 
 #include <android/binder_ibinder.h>
+#include <android/binder_parcel.h>
+#include <android/binder_status.h>
+
+/* Platform only */
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
-#include <android/binder_parcel.h>
 #include <android/binder_parcel_platform.h>
 #include <android/binder_process.h>
 #include <android/binder_rpc.h>
 #include <android/binder_shell.h>
 #include <android/binder_stability.h>
-#include <android/binder_status.h>
+#endif
 
 namespace android {
 
@@ -81,8 +85,10 @@
 
 enum {
     FLAG_ONEWAY = FLAG_ONEWAY,
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
     FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
     FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL,
+#endif
 };
 
 } // namespace consts
diff --git a/libs/binder/rust/sys/Cargo.toml b/libs/binder/rust/sys/Cargo.toml
new file mode 100644
index 0000000..ad8e9c2
--- /dev/null
+++ b/libs/binder/rust/sys/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "android-binder-ndk-sys"
+version = "0.1.0"
+edition = "2021"
+description = "Bindgen bindings to android binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+
+[lib]
+path = "lib.rs"
+
+[build-dependencies]
+bindgen = "0.70.1"
diff --git a/libs/binder/rust/sys/build.rs b/libs/binder/rust/sys/build.rs
new file mode 100644
index 0000000..cb9c65b
--- /dev/null
+++ b/libs/binder/rust/sys/build.rs
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use std::env;
+use std::path::PathBuf;
+
+fn main() {
+    let ndk_home = PathBuf::from(env::var("ANDROID_NDK_HOME").unwrap());
+    let toolchain = ndk_home.join("toolchains/llvm/prebuilt/linux-x86_64/");
+    let sysroot = toolchain.join("sysroot");
+    let bindings = bindgen::Builder::default()
+        .clang_arg(format!("--sysroot={}", sysroot.display()))
+        // TODO figure out what the "standard" #define is and use that instead
+        .header("BinderBindings.hpp")
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        // Keep in sync with libbinder_ndk_bindgen_flags.txt
+        .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
+        .constified_enum("android::c_interface::consts::.*")
+        .allowlist_type("android::c_interface::.*")
+        .allowlist_type("AStatus")
+        .allowlist_type("AIBinder_Class")
+        .allowlist_type("AIBinder")
+        .allowlist_type("AIBinder_Weak")
+        .allowlist_type("AIBinder_DeathRecipient")
+        .allowlist_type("AParcel")
+        .allowlist_type("binder_status_t")
+        .blocklist_function("vprintf")
+        .blocklist_function("strtold")
+        .blocklist_function("_vtlog")
+        .blocklist_function("vscanf")
+        .blocklist_function("vfprintf_worker")
+        .blocklist_function("vsprintf")
+        .blocklist_function("vsnprintf")
+        .blocklist_function("vsnprintf_filtered")
+        .blocklist_function("vfscanf")
+        .blocklist_function("vsscanf")
+        .blocklist_function("vdprintf")
+        .blocklist_function("vasprintf")
+        .blocklist_function("strtold_l")
+        .allowlist_function(".*")
+        .generate()
+        .expect("Couldn't generate bindings");
+    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+    bindings.write_to_file(out_path.join("bindings.rs")).expect("Couldn't write bindings.");
+    println!("cargo::rustc-link-lib=binder_ndk");
+}
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 5352473..349e5a9 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -20,6 +20,7 @@
 use std::fmt;
 
 #[cfg(not(target_os = "trusty"))]
+#[allow(bad_style)]
 mod bindings {
     include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
 }
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 49f4cba..fdc39ed 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -52,19 +52,18 @@
 namespace {
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-// RAII wrapper to defer arbitrary work until the Deferred instance is deleted.
-template <class F>
-class Deferred {
+template <class Mutex>
+class UnlockGuard {
 public:
-    explicit Deferred(F f) : mF{std::move(f)} {}
+    explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); }
 
-    ~Deferred() { mF(); }
+    ~UnlockGuard() { mLock.lock(); }
 
-    Deferred(const Deferred&) = delete;
-    Deferred& operator=(const Deferred&) = delete;
+    UnlockGuard(const UnlockGuard&) = delete;
+    UnlockGuard& operator=(const UnlockGuard&) = delete;
 
 private:
-    F mF;
+    Mutex& mLock;
 };
 #endif
 
@@ -271,9 +270,6 @@
 void BLASTBufferQueue::onFirstRef() {
     // safe default, most producers are expected to override this
     mProducer->setMaxDequeuedBufferCount(2);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-    mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this));
-#endif
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -297,11 +293,16 @@
     mSurfaceControl = surface;
     SurfaceComposerClient::Transaction t;
     if (surfaceControlChanged) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        // SELinux policy may prevent this process from sending the BufferReleaseChannel's file
+        // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This
+        // transaction is applied separately to ensure we don't lose the other updates.
+        t.setApplyToken(mApplyToken)
+                .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer)
+                .apply(false /* synchronous */, true /* oneWay */);
+#endif
         t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
                    layer_state_t::eEnableBackpressure);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-        t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer);
-#endif
         applyTransaction = true;
     }
     mTransformHint = mSurfaceControl->getTransformHint();
@@ -325,7 +326,7 @@
     }
     if (applyTransaction) {
         // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
-        t.setApplyToken(mApplyToken).apply(false, true);
+        t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */);
     }
 }
 
@@ -419,7 +420,6 @@
                                                     stat.latchTime,
                                                     stat.frameEventStats.dequeueReadyTime);
                 }
-#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
                 auto currFrameNumber = stat.frameEventStats.frameNumber;
                 std::vector<ReleaseCallbackId> staleReleases;
                 for (const auto& [key, value]: mSubmitted) {
@@ -435,7 +435,6 @@
                                                 stat.currentMaxAcquiredBufferCount,
                                                 true /* fakeRelease */);
                 }
-#endif
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
             }
@@ -469,6 +468,9 @@
             return;
         }
         bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+        bbq->drainBufferReleaseConsumer();
+#endif
     };
 }
 
@@ -535,8 +537,6 @@
                                      const sp<Fence>& releaseFence) {
     auto it = mSubmitted.find(callbackId);
     if (it == mSubmitted.end()) {
-        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
-                 callbackId.to_string().c_str());
         return;
     }
     mNumAcquired--;
@@ -646,12 +646,7 @@
                            bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
                            bufferItem.mScalingMode, crop);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-    ReleaseBufferCallback releaseBufferCallback =
-            applyTransaction ? nullptr : makeReleaseBufferCallbackThunk();
-#else
     auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
-#endif
     sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
 
     nsecs_t dequeueTime = -1;
@@ -1230,12 +1225,7 @@
         // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
         // we don't miss an interrupt.
         bbq->mBufferReleaseReader->clearInterrupts();
-        bbq->mThreadsBlockingOnDequeue++;
-        bufferQueueLock.unlock();
-        Deferred cleanup{[&]() {
-            bufferQueueLock.lock();
-            bbq->mThreadsBlockingOnDequeue--;
-        }};
+        UnlockGuard unlockGuard{bufferQueueLock};
 
         ATRACE_FORMAT("waiting for free buffer");
         ReleaseCallbackId id;
@@ -1345,6 +1335,21 @@
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
 
+void BLASTBufferQueue::drainBufferReleaseConsumer() {
+    ATRACE_CALL();
+    while (true) {
+        ReleaseCallbackId id;
+        sp<Fence> fence;
+        uint32_t maxAcquiredBufferCount;
+        status_t status =
+                mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount);
+        if (status != OK) {
+            return;
+        }
+        releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+    }
+}
+
 BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} {
     mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)};
     LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
@@ -1438,95 +1443,6 @@
     }
 }
 
-BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) {
-    android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)};
-    LOG_ALWAYS_FATAL_IF(!epollFd.ok(),
-                        "Failed to create buffer release background thread epoll file descriptor. "
-                        "errno=%d message='%s'",
-                        errno, strerror(errno));
-
-    epoll_event registerEndpointFd{};
-    registerEndpointFd.events = EPOLLIN;
-    registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd();
-    status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(),
-                                &registerEndpointFd);
-    LOG_ALWAYS_FATAL_IF(status == -1,
-                        "Failed to register background thread buffer release consumer file "
-                        "descriptor with epoll. errno=%d message='%s'",
-                        errno, strerror(errno));
-
-    // EventFd is used to break the background thread's loop.
-    android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)};
-    LOG_ALWAYS_FATAL_IF(!eventFd.ok(),
-                        "Failed to create background thread buffer release event file descriptor. "
-                        "errno=%d message='%s'",
-                        errno, strerror(errno));
-
-    epoll_event registerEventFd{};
-    registerEventFd.events = EPOLLIN;
-    registerEventFd.data.fd = eventFd.get();
-    status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &registerEventFd);
-    LOG_ALWAYS_FATAL_IF(status == -1,
-                        "Failed to register background thread event file descriptor with epoll. "
-                        "errno=%d message='%s'",
-                        errno, strerror(errno));
-
-    mEventFd = eventFd.get();
-
-    std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd),
-                 weakBbq = wp<BLASTBufferQueue>(bbq)]() {
-        pthread_setname_np(pthread_self(), "BufferReleaseThread");
-        while (true) {
-            epoll_event event{};
-            int eventCount;
-            do {
-                eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/);
-            } while (eventCount == -1 && errno != EINTR);
-
-            if (eventCount == -1) {
-                ALOGE("epoll_wait error while waiting for buffer release in background thread. "
-                      "errno=%d message='%s'",
-                      errno, strerror(errno));
-                continue;
-            }
-
-            // EventFd is used to join this thread.
-            if (event.data.fd == eventFd.get()) {
-                return;
-            }
-
-            sp<BLASTBufferQueue> bbq = weakBbq.promote();
-            if (!bbq) {
-                return;
-            }
-
-            // If there are threads blocking on dequeue, give those threads priority for handling
-            // the release.
-            if (bbq->mThreadsBlockingOnDequeue > 0) {
-                std::this_thread::sleep_for(0ms);
-                continue;
-            }
-
-            ReleaseCallbackId id;
-            sp<Fence> fence;
-            uint32_t maxAcquiredBufferCount;
-            status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence,
-                                                                            maxAcquiredBufferCount);
-            if (status != OK) {
-                ALOGE("failed to read from buffer release consumer in background thread. errno=%d "
-                      "message='%s'",
-                      errno, strerror(errno));
-                continue;
-            }
-            bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
-        }
-    }).detach();
-}
-
-BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
-    eventfd_write(mEventFd, 1);
-}
-
 #endif
 
 } // namespace android
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9c6ef3..e9cb013 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -35,35 +35,35 @@
 namespace {
 
 template <typename T>
-static void readAligned(const void*& buffer, size_t& size, T& value) {
+void readAligned(const void*& buffer, size_t& size, T& value) {
     size -= FlattenableUtils::align<alignof(T)>(buffer);
     FlattenableUtils::read(buffer, size, value);
 }
 
 template <typename T>
-static void writeAligned(void*& buffer, size_t& size, T value) {
+void writeAligned(void*& buffer, size_t& size, T value) {
     size -= FlattenableUtils::align<alignof(T)>(buffer);
     FlattenableUtils::write(buffer, size, value);
 }
 
 template <typename T>
-static void addAligned(size_t& size, T /* value */) {
+void addAligned(size_t& size, T /* value */) {
     size = FlattenableUtils::align<sizeof(T)>(size);
     size += sizeof(T);
 }
 
 template <typename T>
-static inline constexpr uint32_t low32(const T n) {
+inline constexpr uint32_t low32(const T n) {
     return static_cast<uint32_t>(static_cast<uint64_t>(n));
 }
 
 template <typename T>
-static inline constexpr uint32_t high32(const T n) {
+inline constexpr uint32_t high32(const T n) {
     return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
 }
 
 template <typename T>
-static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
     return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
 }
 
@@ -139,19 +139,18 @@
     std::lock_guard lock{mMutex};
     Message message;
     mFlattenedBuffer.resize(message.getFlattenedSize());
-    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
 
     iovec iov{
             .iov_base = mFlattenedBuffer.data(),
             .iov_len = mFlattenedBuffer.size(),
     };
 
-    msghdr msg{
-            .msg_iov = &iov,
-            .msg_iovlen = 1,
-            .msg_control = controlMessageBuffer.data(),
-            .msg_controllen = controlMessageBuffer.size(),
-    };
+    msghdr msg{};
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = controlMessageBuffer.data();
+    msg.msg_controllen = controlMessageBuffer.size();
 
     ssize_t result;
     do {
@@ -161,7 +160,7 @@
         if (errno == EWOULDBLOCK || errno == EAGAIN) {
             return WOULD_BLOCK;
         }
-        ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+        ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno));
         return UNKNOWN_ERROR;
     }
 
@@ -200,9 +199,9 @@
     return OK;
 }
 
-int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
-                                                              const sp<Fence>& fence,
-                                                              uint32_t maxAcquiredBufferCount) {
+status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(
+        const ReleaseCallbackId& callbackId, const sp<Fence>& fence,
+        uint32_t maxAcquiredBufferCount) {
     Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
     mFlattenedBuffer.resize(message.getFlattenedSize());
     int flattenedFd;
@@ -213,25 +212,22 @@
         size_t flattenedBufferSize = mFlattenedBuffer.size();
         int* flattenedFdPtr = &flattenedFd;
         size_t flattenedFdCount = 1;
-        if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
-                                           flattenedFdCount);
-            err != OK) {
-            ALOGE("Failed to flatten BufferReleaseChannel message.");
-            return err;
+        if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize,
+                                              flattenedFdPtr, flattenedFdCount);
+            status != OK) {
+            return status;
         }
     }
 
-    iovec iov{
-            .iov_base = mFlattenedBuffer.data(),
-            .iov_len = mFlattenedBuffer.size(),
-    };
+    iovec iov{};
+    iov.iov_base = mFlattenedBuffer.data();
+    iov.iov_len = mFlattenedBuffer.size();
 
-    msghdr msg{
-            .msg_iov = &iov,
-            .msg_iovlen = 1,
-    };
+    msghdr msg{};
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
 
-    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
     if (fence && fence->isValid()) {
         msg.msg_control = controlMessageBuffer.data();
         msg.msg_controllen = controlMessageBuffer.size();
@@ -248,7 +244,6 @@
         result = sendmsg(mFd, &msg, 0);
     } while (result == -1 && errno == EINTR);
     if (result == -1) {
-        ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
         return -errno;
     }
 
@@ -344,13 +339,6 @@
         return -errno;
     }
 
-    // Make the producer write-only
-    if (shutdown(producerFd.get(), SHUT_RD) == -1) {
-        ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
-              name.c_str(), errno, strerror(errno));
-        return -errno;
-    }
-
     outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
     outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
     return STATUS_OK;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 422c57b..4b53134 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -664,6 +664,10 @@
         what |= eShadowRadiusChanged;
         shadowRadius = other.shadowRadius;
     }
+    if (other.what & eLutsChanged) {
+        what |= eLutsChanged;
+        luts = other.luts;
+    }
     if (other.what & eDefaultFrameRateCompatibilityChanged) {
         what |= eDefaultFrameRateCompatibilityChanged;
         defaultFrameRateCompatibility = other.defaultFrameRateCompatibility;
@@ -821,6 +825,8 @@
     CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
     CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
     if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
+    if (other.what & eLutsChanged) diff |= eLutsChanged;
+
     return diff;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index eeea80f..a93fc92 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -29,6 +29,7 @@
 #include <android/gui/IWindowInfosListener.h>
 #include <android/gui/TrustedPresentationThresholds.h>
 #include <android/os/IInputConstants.h>
+#include <gui/DisplayLuts.h>
 #include <gui/FrameRateUtils.h>
 #include <gui/TraceUtils.h>
 #include <utils/Errors.h>
@@ -1940,15 +1941,19 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
-        const sp<SurfaceControl>& sc, const base::unique_fd& /*lutFd*/,
-        const std::vector<int32_t>& /*offsets*/, const std::vector<int32_t>& /*dimensions*/,
-        const std::vector<int32_t>& /*sizes*/, const std::vector<int32_t>& /*samplingKeys*/) {
+        const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+        const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions,
+        const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    // TODO (b/329472856): update layer_state_t for lut(s)
+
+    s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
+                                                 dimensions, sizes, samplingKeys);
+    s->what |= layer_state_t::eLutsChanged;
+
     registerSurfaceControlForCallback(sc);
     return *this;
 }
diff --git a/libs/gui/aidl/android/gui/Lut.aidl b/libs/gui/aidl/android/gui/Lut.aidl
deleted file mode 100644
index a06e521..0000000
--- a/libs/gui/aidl/android/gui/Lut.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.gui;
-
-import android.gui.LutProperties;
-import android.os.ParcelFileDescriptor;
-
-/**
- * This mirrors aidl::android::hardware::graphics::composer3::Lut definition
- * @hide
- */
-parcelable Lut {
-    @nullable ParcelFileDescriptor pfd;
-
-    LutProperties lutProperties;
-}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl
index 561e0c0..87b878c 100644
--- a/libs/gui/aidl/android/gui/LutProperties.aidl
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -25,7 +25,7 @@
     enum Dimension { ONE_D = 1, THREE_D = 3 }
     Dimension dimension;
 
-    long size;
+    int size;
     @Backing(type="int")
     enum SamplingKey { RGB, MAX_RGB }
     SamplingKey[] samplingKeys;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 99c64da..4fd44e5 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -325,6 +325,8 @@
     std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer;
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
 
+    void drainBufferReleaseConsumer();
+
     class BufferReleaseReader {
     public:
         explicit BufferReleaseReader(BLASTBufferQueue&);
@@ -353,19 +355,6 @@
     };
 
     std::optional<BufferReleaseReader> mBufferReleaseReader;
-
-    std::atomic<int> mThreadsBlockingOnDequeue = 0;
-
-    class BufferReleaseThread {
-    public:
-        BufferReleaseThread(const sp<BLASTBufferQueue>&);
-        ~BufferReleaseThread();
-
-    private:
-        int mEventFd;
-    };
-
-    std::optional<BufferReleaseThread> mBufferReleaseThread;
 #endif
 };
 
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
new file mode 100644
index 0000000..16a360d
--- /dev/null
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <vector>
+
+namespace android::gui {
+
+struct DisplayLuts {
+public:
+    struct Entry {
+        int32_t dimension;
+        int32_t size;
+        int32_t samplingKey;
+    };
+
+    DisplayLuts() {}
+
+    DisplayLuts(base::unique_fd lutfd, std::vector<int32_t> lutoffsets,
+                std::vector<int32_t> lutdimensions, std::vector<int32_t> lutsizes,
+                std::vector<int32_t> lutsamplingKeys) {
+        fd = std::move(lutfd);
+        offsets = lutoffsets;
+        lutProperties.reserve(offsets.size());
+        for (size_t i = 0; i < lutoffsets.size(); i++) {
+            Entry entry{lutdimensions[i], lutsizes[i], lutsamplingKeys[i]};
+            lutProperties.emplace_back(entry);
+        }
+    }
+
+    base::unique_fd& getLutFileDescriptor() { return fd; }
+
+    std::vector<Entry> lutProperties;
+    std::vector<int32_t> offsets;
+
+private:
+    base::unique_fd fd;
+}; // struct DisplayLuts
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 735375a..34350d2 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -17,8 +17,20 @@
 #pragma once
 
 #include <com_android_graphics_libgui_flags.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 
 #define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES                  \
     (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
      COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) &&  \
-     COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file
+     COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
+
+#define WB_LIBCAMERASERVICE_WITH_DEPENDENCIES       \
+    (WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES && \
+     COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_LIBCAMERASERVICE))
+
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+typedef android::Surface SurfaceType;
+#else
+typedef android::IGraphicBufferProducer SurfaceType;
+#endif
\ No newline at end of file
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 00065c8..6bfeaec 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,6 +26,7 @@
 #include <android/gui/LayerCaptureArgs.h>
 #include <android/gui/TrustedPresentationThresholds.h>
 #include <android/native_window.h>
+#include <gui/DisplayLuts.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
@@ -184,6 +185,7 @@
         eCachingHintChanged = 0x00000200,
         eDimmingEnabledChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
+        eLutsChanged = 0x00001000,
         eBufferCropChanged = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
@@ -255,7 +257,7 @@
             layer_state_t::eTransformToDisplayInverseChanged |
             layer_state_t::eTransparentRegionChanged |
             layer_state_t::eExtendedRangeBrightnessChanged |
-            layer_state_t::eDesiredHdrHeadroomChanged;
+            layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged;
 
     // Content updates.
     static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES |
@@ -416,6 +418,8 @@
     TrustedPresentationListener trustedPresentationListener;
 
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
+
+    std::shared_ptr<gui::DisplayLuts> luts;
 };
 
 class ComposerState {
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index 7ddac81..7c762d3 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -24,7 +24,9 @@
 #include <binder/IBinder.h>
 #include <binder/Parcelable.h>
 
+#include <gui/Flags.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 
 namespace android {
 
@@ -46,6 +48,14 @@
     sp<IGraphicBufferProducer> graphicBufferProducer;
     sp<IBinder> surfaceControlHandle;
 
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    // functions used to convert to a parcelable Surface so it can be passed over binder.
+    static Surface fromSurface(const sp<android::Surface>& surface);
+    sp<android::Surface> toSurface() const;
+
+    status_t getUniqueId(/* out */ uint64_t* id) const;
+#endif
+
     virtual status_t writeToParcel(Parcel* parcel) const override;
     virtual status_t readFromParcel(const Parcel* parcel) override;
 
diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
index 11d122b..74f69e1 100644
--- a/libs/gui/tests/BufferReleaseChannel_test.cpp
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -29,11 +29,11 @@
 
 // Helper function to check if two file descriptors point to the same file.
 bool is_same_file(int fd1, int fd2) {
-    struct stat stat1;
+    struct stat stat1 {};
     if (fstat(fd1, &stat1) != 0) {
         return false;
     }
-    struct stat stat2;
+    struct stat stat2 {};
     if (fstat(fd2, &stat2) != 0) {
         return false;
     }
@@ -42,7 +42,18 @@
 
 } // namespace
 
-TEST(BufferReleaseChannelTest, MessageFlattenable) {
+class BufferReleaseChannelTest : public testing::Test {
+protected:
+    std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer;
+    std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer;
+
+    void SetUp() override {
+        ASSERT_EQ(OK,
+                  BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer));
+    }
+};
+
+TEST_F(BufferReleaseChannelTest, MessageFlattenable) {
     ReleaseCallbackId releaseCallbackId{1, 2};
     sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
     uint32_t maxAcquiredBufferCount = 5;
@@ -92,31 +103,23 @@
 
 // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
 // available.
-TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
-    std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
-    std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
-    ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
     ReleaseCallbackId releaseCallbackId;
     sp<Fence> releaseFence;
     uint32_t maxAcquiredBufferCount;
     ASSERT_EQ(WOULD_BLOCK,
-              consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+              mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
 }
 
 // Verify that we can write a message to the BufferReleaseChannel producer and read that message
 // using the BufferReleaseChannel consumer.
-TEST(BufferReleaseChannelTest, ProduceAndConsume) {
-    std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
-    std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
-    ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ProduceAndConsume) {
     sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
 
     for (uint64_t i = 0; i < 64; i++) {
         ReleaseCallbackId producerId{i, i + 1};
         uint32_t maxAcquiredBufferCount = i + 2;
-        ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+        ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
     }
 
     for (uint64_t i = 0; i < 64; i++) {
@@ -127,7 +130,7 @@
         sp<Fence> consumerFence;
         uint32_t maxAcquiredBufferCount;
         ASSERT_EQ(OK,
-                  consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+                  mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
 
         ASSERT_EQ(expectedId, consumerId);
         ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
@@ -135,4 +138,16 @@
     }
 }
 
+// Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to.
+TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) {
+    uint64_t data = 0;
+    ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t)));
+    ASSERT_EQ(errno, EPIPE);
+}
+
+// Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from.
+TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) {
+    ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t)));
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 7d0b512..a481d12 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -294,7 +294,7 @@
                     transactionBody) {
         SurfaceComposerClient::Transaction t;
         transactionBody(t, mSurfaceControl);
-        t.apply(true);
+        t.apply(/*synchronously=*/true);
     }
 
     virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
@@ -307,7 +307,7 @@
         t.setAlpha(mSurfaceControl, 1);
         auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
         t.addWindowInfosReportedListener(reportedListener);
-        t.apply();
+        t.apply(/*synchronously=*/true);
         reportedListener->wait();
     }
 
@@ -319,7 +319,7 @@
         request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         request.displayId = displayId.val();
         t.setFocusedWindow(request);
-        t.apply(true);
+        t.apply(/*synchronously=*/true);
     }
 
 public:
@@ -363,7 +363,7 @@
                     transactionBody) override {
         SurfaceComposerClient::Transaction t;
         transactionBody(t, mParentSurfaceControl);
-        t.apply(true);
+        t.apply(/*synchronously=*/true);
     }
 
     void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
@@ -377,7 +377,7 @@
         t.setInputWindowInfo(mSurfaceControl, mInputInfo);
         t.setCrop(mSurfaceControl, crop);
         t.setAlpha(mSurfaceControl, 1);
-        t.apply(true);
+        t.apply(/*synchronously=*/true);
     }
 
 private:
@@ -417,7 +417,7 @@
                 BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
         sp<GraphicBuffer> buffer =
                 new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
-        Transaction().setBuffer(layer, buffer).apply(true);
+        Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true);
         usleep(mBufferPostDelay);
     }
 
@@ -1207,7 +1207,7 @@
         t.setDisplayLayerStack(token, layerStack);
         t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height},
                                {offsetX, offsetY, offsetX + width, offsetY + height});
-        t.apply(true);
+        t.apply(/*synchronously=*/true);
 
         mVirtualDisplays.push_back(token);
     }
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 84c2a6a..9f57923 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,6 +121,38 @@
     return str.value_or(String16());
 }
 
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+Surface Surface::fromSurface(const sp<android::Surface>& surface) {
+    if (surface == nullptr) {
+        ALOGE("%s: Error: view::Surface::fromSurface failed due to null surface.", __FUNCTION__);
+        return Surface();
+    }
+    Surface s;
+    s.name = String16(surface->getConsumerName());
+    s.graphicBufferProducer = surface->getIGraphicBufferProducer();
+    s.surfaceControlHandle = surface->getSurfaceControlHandle();
+    return s;
+}
+
+sp<android::Surface> Surface::toSurface() const {
+    if (graphicBufferProducer == nullptr) return nullptr;
+    return new android::Surface(graphicBufferProducer, false, surfaceControlHandle);
+}
+
+status_t Surface::getUniqueId(uint64_t* out_id) const {
+    if (graphicBufferProducer == nullptr) {
+        ALOGE("android::viewSurface::getUniqueId() failed because it's not initialized.");
+        return UNEXPECTED_NULL;
+    }
+    status_t status = graphicBufferProducer->getUniqueId(out_id);
+    if (status != OK) {
+        ALOGE("android::viewSurface::getUniqueId() failed.");
+        return status;
+    }
+    return OK;
+}
+#endif
+
 std::string Surface::toString() const {
     std::stringstream out;
     out << name;
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 9665de7..d3653cf 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -357,7 +357,8 @@
                 mBatches[deviceId].emplace(msg);
             } else {
                 // consume all pending batches for this device immediately
-                consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
+                consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/
+                                          std::numeric_limits<nsecs_t>::max());
                 if (canResample &&
                     (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) {
                     LOG_IF(INFO, mResamplers.erase(deviceId) == 0)
@@ -480,7 +481,7 @@
 }
 
 std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
-InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
+InputConsumerNoResampling::createBatchedMotionEvent(const std::optional<nsecs_t> requestedFrameTime,
                                                     std::queue<InputMessage>& messages) {
     std::unique_ptr<MotionEvent> motionEvent;
     std::optional<uint32_t> firstSeqForBatch;
@@ -491,7 +492,11 @@
     const nanoseconds resampleLatency = (resampler != mResamplers.cend())
             ? resampler->second->getResampleLatency()
             : nanoseconds{0};
-    const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
+    // When batching is not enabled, we want to consume all events. That's equivalent to having an
+    // infinite requestedFrameTime.
+    const nanoseconds adjustedFrameTime = (requestedFrameTime.has_value())
+            ? (nanoseconds{*requestedFrameTime} - resampleLatency)
+            : nanoseconds{std::numeric_limits<nsecs_t>::max()};
 
     while (!messages.empty() &&
            (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
@@ -513,8 +518,9 @@
     if (!messages.empty()) {
         futureSample = &messages.front();
     }
-    if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) {
-        resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+    if ((motionEvent != nullptr) && (resampler != mResamplers.cend()) &&
+        (requestedFrameTime.has_value())) {
+        resampler->second->resampleMotionEvent(nanoseconds{*requestedFrameTime}, *motionEvent,
                                                futureSample);
     }
 
@@ -524,16 +530,13 @@
 bool InputConsumerNoResampling::consumeBatchedInputEvents(
         std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
     ensureCalledOnLooperThread(__func__);
-    // When batching is not enabled, we want to consume all events. That's equivalent to having an
-    // infinite requestedFrameTime.
-    requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
     bool producedEvents = false;
 
     for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
                                                     : (mBatches.begin());
          deviceIdIter != mBatches.cend(); ++deviceIdIter) {
         std::queue<InputMessage>& messages = deviceIdIter->second;
-        auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
+        auto [motion, firstSeqForBatch] = createBatchedMotionEvent(requestedFrameTime, messages);
         if (motion != nullptr) {
             LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
             mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e93c04d..fd77048 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -94,13 +94,6 @@
 }
 
 flag {
-  name: "enable_new_mouse_pointer_ballistics"
-  namespace: "input"
-  description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
-  bug: "315313622"
-}
-
-flag {
   name: "rate_limit_user_activity_poke_in_dispatcher"
   namespace: "input"
   description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher."
@@ -221,3 +214,20 @@
   description: "Set input device's power/wakeup sysfs node"
   bug: "372812925"
 }
+
+flag {
+  name: "enable_alphabetic_keyboard_wake"
+  namespace: "input"
+  description: "Enable wake from alphabetic keyboards."
+  bug: "352856881"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "connected_displays_cursor"
+  namespace: "lse_desktop_experience"
+  description: "Allow cursor to transition across multiple connected displays"
+  bug: "362719483"
+}
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index 883ca82..97688a8 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -38,6 +38,8 @@
 using std::chrono::nanoseconds;
 using namespace std::chrono_literals;
 
+const std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
 struct Pointer {
     int32_t id{0};
     float x{0.0f};
@@ -440,7 +442,7 @@
             {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
 
     invokeLooperCallback();
-    mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+    mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
 
     // MotionEvent should not resampled because the resample time falls exactly on the existing
     // event time.
@@ -496,14 +498,15 @@
             {40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
 
     invokeLooperCallback();
-    mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+    mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+    // Original and resampled event should be both overwritten.
     assertReceivedMotionEvent(
             {InputEventEntry{40ms,
                              {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
-                             AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+                             AMOTION_EVENT_ACTION_MOVE},
              InputEventEntry{45ms,
                              {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
-                             AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+                             AMOTION_EVENT_ACTION_MOVE}});
 
     mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
     mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -552,13 +555,14 @@
 
     invokeLooperCallback();
     mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+    // Original and resampled event should be both overwritten.
     assertReceivedMotionEvent(
             {InputEventEntry{24ms,
                              {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
-                             AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+                             AMOTION_EVENT_ACTION_MOVE},
              InputEventEntry{26ms,
                              {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
-                             AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+                             AMOTION_EVENT_ACTION_MOVE}});
 
     mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
     mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -566,4 +570,175 @@
     mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
 }
 
+TEST_F(InputConsumerResamplingTest, DoNotResampleWhenFrameTimeIsNotAvailable) {
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent({InputEventEntry{0ms,
+                                               {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+                                               AMOTION_EVENT_ACTION_DOWN}});
+
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(std::nullopt);
+    assertReceivedMotionEvent({InputEventEntry{10ms,
+                                               {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+                                               AMOTION_EVENT_ACTION_MOVE},
+                               InputEventEntry{20ms,
+                                               {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+                                               AMOTION_EVENT_ACTION_MOVE}});
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, TwoPointersAreResampledIndependently) {
+    // Full action for when a pointer with index=1 appears (some other pointer must already be
+    // present)
+    const int32_t actionPointer1Down =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+    // Full action for when a pointer with index=0 disappears (some other pointer must still remain)
+    const int32_t actionPointer0Up =
+            AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {0ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+    mClientTestChannel->assertNoSentMessages();
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent({InputEventEntry{0ms,
+                                               {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+                                               AMOTION_EVENT_ACTION_DOWN}});
+
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {10ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{10ms + RESAMPLE_LATENCY}.count());
+    // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime
+    assertReceivedMotionEvent({InputEventEntry{10ms,
+                                               {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+                                               AMOTION_EVENT_ACTION_MOVE}});
+
+    // Second pointer id=1 appears
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({15ms,
+                                {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+                                 Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+                                actionPointer1Down}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
+    // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime.
+    assertReceivedMotionEvent({InputEventEntry{15ms,
+                                               {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+                                                Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+                                               actionPointer1Down}});
+
+    // Both pointers move
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({30ms,
+                                {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+                                 Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+                                AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({40ms,
+                                {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+                                 Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                                AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+    assertReceivedMotionEvent(
+            {InputEventEntry{30ms,
+                             {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+                              Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{40ms,
+                             {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+                              Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{45ms,
+                             {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+                              Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}});
+
+    // Both pointers move again
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({60ms,
+                                {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+                                 Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                                AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({70ms,
+                                {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+                                 Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+                                AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{75ms + RESAMPLE_LATENCY}.count());
+
+    /*
+     * The pointer id 0 at t = 60 should not be equal to 120 because the value was received twice,
+     * and resampled to 130. Therefore, if we reported 130, then we should continue to report it as
+     * such. Likewise, with pointer id 1.
+     */
+
+    // Not 120 because it matches a previous real event.
+    assertReceivedMotionEvent(
+            {InputEventEntry{60ms,
+                             {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+                              Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{70ms,
+                             {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+                              Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{75ms,
+                             {Pointer{.id = 0, .x = 135.0f, .y = 135.0f, .isResampled = true},
+                              Pointer{.id = 1, .x = 750.0f, .y = 750.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}});
+
+    // First pointer id=0 leaves the screen
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage({80ms,
+                                {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+                                 Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                                actionPointer0Up}));
+
+    invokeLooperCallback();
+    // Not resampled event for ACTION_POINTER_UP
+    assertReceivedMotionEvent({InputEventEntry{80ms,
+                                               {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+                                                Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                                               actionPointer0Up}});
+
+    // Remaining pointer id=1 is still present, but doesn't move
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {90ms, {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{100ms}.count());
+
+    /*
+     * The latest event with ACTION_MOVE was at t = 70 with value = 700. Thus, the resampled value
+     * is 700 + ((95 - 70)/(90 - 70))*(600 - 700) = 575.
+     */
+    assertReceivedMotionEvent(
+            {InputEventEntry{90ms,
+                             {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+                             AMOTION_EVENT_ACTION_MOVE},
+             InputEventEntry{95ms,
+                             {Pointer{.id = 1, .x = 575.0f, .y = 575.0f, .isResampled = true}},
+                             AMOTION_EVENT_ACTION_MOVE}});
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 6a3bbe5..06e19bb 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -194,7 +194,7 @@
     std::unique_ptr<MotionEvent> moveMotionEvent =
             assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
     ASSERT_NE(moveMotionEvent, nullptr);
-    EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
+    EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 2UL);
 
     mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
     mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
@@ -443,4 +443,5 @@
     mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true);
     mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true);
 }
+
 } // namespace android
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 8d8b530..9841c03 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -571,11 +571,12 @@
     std::chrono::nanoseconds frameTime;
     std::vector<InputEventEntry> entries, expectedEntries;
 
-    // full action for when a pointer with id=1 appears (some other pointer must already be present)
+    // full action for when a pointer with index=1 appears (some other pointer must already be
+    // present)
     constexpr int32_t actionPointer1Down =
             AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-    // full action for when a pointer with id=0 disappears (some other pointer must still remain)
+    // full action for when a pointer with index=0 disappears (some other pointer must still remain)
     constexpr int32_t actionPointer0Up =
             AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index f97eed5..ac3a832 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -263,6 +263,16 @@
     return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
 }
 
+int32_t ANativeWindow_setFrameRateParams(
+        ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+        ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+        return -EINVAL;
+    }
+    return native_window_set_frame_rate_params(window, desiredMinRate, desiredMaxRate,
+                                               fixedSourceRate, changeFrameRateStrategy);
+}
+
 /**************************************************************************************************
  * vndk-stable
  **************************************************************************************************/
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index be6623e..bd8d67a 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -33,8 +33,8 @@
 #ifndef ANDROID_NATIVE_WINDOW_H
 #define ANDROID_NATIVE_WINDOW_H
 
-#include <stdint.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #include <android/data_space.h>
@@ -282,7 +282,7 @@
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
 
 /** Change frame rate strategy value for ANativeWindow_setFrameRate. */
-enum ANativeWindow_ChangeFrameRateStrategy {
+typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t {
     /**
      * Change the frame rate only if the transition is going to be seamless.
      */
@@ -292,7 +292,7 @@
      * i.e. with visual interruptions for the user.
      */
     ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
-} __INTRODUCED_IN(31);
+} ANativeWindow_ChangeFrameRateStrategy __INTRODUCED_IN(31);
 
 /**
  * Sets the intended frame rate for this window.
@@ -345,6 +345,76 @@
         __INTRODUCED_IN(31);
 
 /**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different frame rates,
+ * the system may choose a display refresh rate to better match this surface's frame
+ * rate. Usage of this API won't introduce frame rate throttling, or affect other
+ * aspects of the application's frame production pipeline. However, because the system
+ * may change the display refresh rate, calls to this function may result in changes
+ * to Choreographer callback timings, and changes to the time interval at which the
+ * system releases buffers back to the application.
+ *
+ * Note that this only has an effect for surfaces presented on the display. If this
+ * surface is consumed by something other than the system compositor, e.g. a media
+ * codec, this call has no effect.
+ *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
+ * See ANativeWindow_clearFrameRate().
+ *
+ * Available since API level 36.
+ *
+ * \param window pointer to an ANativeWindow object.
+ *
+ * \param desiredMinRate The desired minimum frame rate (inclusive) for the window, specifying that
+ * the surface prefers the device render rate to be at least `desiredMinRate`.
+ *
+ * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate.
+ *
+ * <p>Set `desiredMinRate` = 0 to indicate the window has no preference
+ * and any frame rate is acceptable.
+ *
+ * <p>The value should be greater than or equal to 0.
+ *
+ * \param desiredMaxRate The desired maximum frame rate (inclusive) for the window, specifying that
+ * the surface prefers the device render rate to be at most `desiredMaxRate`.
+ *
+ * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate.
+ *
+ * <p>Set `desiredMaxRate` = positive infinity to indicate the window has no preference
+ * and any frame rate is acceptable.
+ *
+ * <p>The value should be greater than or equal to `desiredMinRate`.
+ *
+ * \param fixedSourceRate The "fixed source" frame rate of the window if the content has an
+ * inherently fixed frame rate, e.g. a video that has a specific frame rate.
+ *
+ * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a
+ * multiple, the surface can render without frame pulldown, for optimal smoothness. For
+ * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps,
+ * 60 fps, 90 fps, 120 fps, and so on.
+ *
+ * <p>Setting the fixed source rate can also be used together with a desired
+ * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still
+ * means the window's content has a fixed frame rate of `fixedSourceRate`, but additionally
+ * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an
+ * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth
+ * animation on the same window which looks good when drawing within a frame rate range such as
+ * [`desiredMinRate`, `desiredMaxRate`] = [60,120].
+ *
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface
+ * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such
+ * as a black screen for a second or two.
+ *
+ * \return 0 for success, -EINVAL if the arguments are invalid.
+ */
+int32_t ANativeWindow_setFrameRateParams(
+        ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+        ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36);
+
+/**
  * Clears the frame rate which is set for this window.
  *
  * This is equivalent to calling
@@ -366,14 +436,13 @@
  *
  * See ANativeWindow_setFrameRateWithChangeStrategy().
  *
- * Available since API level 34.
+ * Available since API level 31.
  *
  * \param window pointer to an ANativeWindow object.
  *
  * \return 0 for success, -EINVAL if the window value is invalid.
  */
-inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
-        __INTRODUCED_IN(__ANDROID_API_U__) {
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) __INTRODUCED_IN(31) {
     return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
             ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
             ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 33c303a..05f49ad 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1156,6 +1156,23 @@
                            (int)compatibility, (int)changeFrameRateStrategy);
 }
 
+static inline int native_window_set_frame_rate_params(struct ANativeWindow* window,
+                                                      float desiredMinRate, float desiredMaxRate,
+                                                      float fixedSourceRate,
+                                                      int8_t changeFrameRateStrategy) {
+    // TODO(b/362798998): Fix plumbing to send whole params
+    int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
+                                             : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+    double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+            ? fixedSourceRate
+            : desiredMinRate;
+    if (desiredMaxRate < desiredMinRate) {
+        return -EINVAL;
+    }
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility,
+                           changeFrameRateStrategy);
+}
+
 struct ANativeWindowFrameTimelineInfo {
     // Frame Id received from ANativeWindow_getNextFrameId.
     uint64_t frameNumber;
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index e29d5a6..071e354 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -53,6 +53,7 @@
     ANativeWindow_setBuffersTransform;
     ANativeWindow_setDequeueTimeout; # systemapi introduced=30
     ANativeWindow_setFrameRate; # introduced=30
+    ANativeWindow_setFrameRateParams; # introduced=36
     ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a1d986e..014c912 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -203,8 +203,8 @@
         Self(buffer_ptr)
     }
 
-    /// Creates a new Rust HardwareBuffer to wrap the given AHardwareBuffer without taking ownership
-    /// of it.
+    /// Creates a new Rust HardwareBuffer to wrap the given `AHardwareBuffer` without taking
+    /// ownership of it.
     ///
     /// Unlike [`from_raw`](Self::from_raw) this method will increment the refcount on the buffer.
     /// This means that the caller can continue to use the raw buffer it passed in, and must call
@@ -220,14 +220,20 @@
         Self(buffer)
     }
 
-    /// Get the internal `AHardwareBuffer` pointer that is only valid when this `HardwareBuffer`
-    /// exists. This can be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+    /// Returns the internal `AHardwareBuffer` pointer.
+    ///
+    /// This is only valid as long as this `HardwareBuffer` exists, so shouldn't be stored. It can
+    /// be used to provide a pointer for a C/C++ API over FFI.
     pub fn as_raw(&self) -> NonNull<AHardwareBuffer> {
         self.0
     }
 
-    /// Get the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
-    /// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+    /// Gets the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
+    /// be used for a C/C++ API which takes ownership of the pointer.
+    ///
+    /// The caller is responsible for releasing the `AHardwareBuffer` pointer by calling
+    /// `AHardwareBuffer_release` when it is finished with it, or may convert it back to a Rust
+    /// `HardwareBuffer` by calling [`HardwareBuffer::from_raw`].
     pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
         let buffer = ManuallyDrop::new(self);
         buffer.0
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 859ae8b..ac43da8 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <gui/DisplayLuts.h>
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/ExternalTexture.h>
@@ -145,6 +146,8 @@
     // If white point nits are unknown, then this layer is assumed to have the
     // same luminance as the brightest layer in the scene.
     float whitePointNits = -1.f;
+
+    std::shared_ptr<gui::DisplayLuts> luts;
 };
 
 // Keep in sync with custom comparison function in
@@ -187,7 +190,7 @@
             lhs.blurRegionTransform == rhs.blurRegionTransform &&
             lhs.stretchEffect == rhs.stretchEffect &&
             lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
-            lhs.whitePointNits == rhs.whitePointNits;
+            lhs.whitePointNits == rhs.whitePointNits && lhs.luts == rhs.luts;
 }
 
 static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 5159ffe..b19a862 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -135,6 +135,9 @@
         "EGL/MultifileBlobCache.cpp",
     ],
     export_include_dirs: ["EGL"],
+    shared_libs: [
+        "libz",
+    ],
 }
 
 cc_library_shared {
@@ -169,6 +172,7 @@
         "libutils",
         "libSurfaceFlingerProp",
         "libunwindstack",
+        "libz",
     ],
     static_libs: [
         "libEGL_getProcAddress",
@@ -199,6 +203,7 @@
     ],
     shared_libs: [
         "libutils",
+        "libz",
     ],
 }
 
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 4a0fac4..573ca54 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -27,6 +27,7 @@
 
 #include <log/log.h>
 #include <utils/Trace.h>
+#include <zlib.h>
 
 // Cache file header
 static const char* cacheFileMagic = "EGL$";
@@ -34,20 +35,10 @@
 
 namespace android {
 
-uint32_t crc32c(const uint8_t* buf, size_t len) {
-    const uint32_t polyBits = 0x82F63B78;
-    uint32_t r = 0;
-    for (size_t i = 0; i < len; i++) {
-        r ^= buf[i];
-        for (int j = 0; j < 8; j++) {
-            if (r & 1) {
-                r = (r >> 1) ^ polyBits;
-            } else {
-                r >>= 1;
-            }
-        }
-    }
-    return r;
+uint32_t GenerateCRC32(const uint8_t *data, size_t size)
+{
+    const unsigned long initialValue = crc32_z(0u, nullptr, 0u);
+    return static_cast<uint32_t>(crc32_z(initialValue, data, size));
 }
 
 FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
@@ -101,7 +92,7 @@
             return;
         }
         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
-        if (crc32c(buf + headerSize, cacheSize) != *crc) {
+        if (GenerateCRC32(buf + headerSize, cacheSize) != *crc) {
             ALOGE("cache file failed CRC check");
             close(fd);
             return;
@@ -175,7 +166,7 @@
         // Write the file magic and CRC
         memcpy(buf, cacheFileMagic, 4);
         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
-        *crc = crc32c(buf + headerSize, cacheSize);
+        *crc = GenerateCRC32(buf + headerSize, cacheSize);
 
         if (write(fd, buf, fileSize) == -1) {
             ALOGE("error writing cache file: %s (%d)", strerror(errno),
diff --git a/opengl/libs/EGL/FileBlobCache.h b/opengl/libs/EGL/FileBlobCache.h
index f083b0d..224444d 100644
--- a/opengl/libs/EGL/FileBlobCache.h
+++ b/opengl/libs/EGL/FileBlobCache.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-uint32_t crc32c(const uint8_t* buf, size_t len);
+uint32_t GenerateCRC32(const uint8_t *data, size_t size);
 
 class FileBlobCache : public BlobCache {
 public:
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 9905210..f7e33b3 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -214,9 +214,8 @@
                 }
 
                 // Ensure we have a good CRC
-                if (header.crc !=
-                    crc32c(mappedEntry + sizeof(MultifileHeader),
-                           fileSize - sizeof(MultifileHeader))) {
+                if (header.crc != GenerateCRC32(mappedEntry + sizeof(MultifileHeader),
+                                                fileSize - sizeof(MultifileHeader))) {
                     ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash);
                     if (remove(fullPath.c_str()) != 0) {
                         ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
@@ -532,9 +531,9 @@
             mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length());
 
     // Finally update the crc, using cacheVersion and everything the follows
-    status.crc =
-            crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
-                   sizeof(status) - offsetof(MultifileStatus, cacheVersion));
+    status.crc = GenerateCRC32(
+        reinterpret_cast<uint8_t *>(&status) + offsetof(MultifileStatus, cacheVersion),
+        sizeof(status) - offsetof(MultifileStatus, cacheVersion));
 
     // Create the status file
     std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
@@ -599,9 +598,9 @@
     }
 
     // Ensure we have a good CRC
-    if (status.crc !=
-        crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
-               sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
+    if (status.crc != GenerateCRC32(reinterpret_cast<uint8_t *>(&status) +
+                                        offsetof(MultifileStatus, cacheVersion),
+                                    sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
         ALOGE("STATUS(CHECK): Cache status failed CRC check!");
         return false;
     }
@@ -840,8 +839,8 @@
 
             // Add CRC check to the header (always do this last!)
             MultifileHeader* header = reinterpret_cast<MultifileHeader*>(buffer);
-            header->crc =
-                    crc32c(buffer + sizeof(MultifileHeader), bufferSize - sizeof(MultifileHeader));
+            header->crc             = GenerateCRC32(buffer + sizeof(MultifileHeader),
+                                                    bufferSize - sizeof(MultifileHeader));
 
             ssize_t result = write(fd, buffer, bufferSize);
             if (result != bufferSize) {
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index 18566c2..65aa2db 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -34,7 +34,7 @@
 
 namespace android {
 
-constexpr uint32_t kMultifileBlobCacheVersion = 1;
+constexpr uint32_t kMultifileBlobCacheVersion = 2;
 constexpr char kMultifileBlobCacheStatusFile[] = "cache.status";
 
 struct MultifileHeader {
diff --git a/opengl/libs/EGL/fuzzer/Android.bp b/opengl/libs/EGL/fuzzer/Android.bp
index 022a2a3..4947e5f 100644
--- a/opengl/libs/EGL/fuzzer/Android.bp
+++ b/opengl/libs/EGL/fuzzer/Android.bp
@@ -36,6 +36,10 @@
         "libutils",
     ],
 
+    shared_libs: [
+        "libz",
+    ],
+
     srcs: [
         "MultifileBlobCache_fuzzer.cpp",
     ],
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 503d7df..839a5ca 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -765,6 +765,30 @@
     }
 }
 
+// Verify that eglCreateContext works when EGL_TELEMETRY_HINT_ANDROID is used with
+// NO_HINT = 0, SKIP_TELEMETRY = 1 and an invalid of value of 2
+TEST_F(EGLTest, EGLContextTelemetryHintExt) {
+    for (int i = 0; i < 3; i++) {
+        EGLConfig config;
+        get8BitConfig(config);
+        std::vector<EGLint> contextAttributes;
+        contextAttributes.reserve(4);
+        contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID);
+        contextAttributes.push_back(i);
+        contextAttributes.push_back(EGL_NONE);
+        contextAttributes.push_back(EGL_NONE);
+
+        EGLContext eglContext = eglCreateContext(mEglDisplay, config, EGL_NO_CONTEXT,
+                                                 contextAttributes.data());
+        EXPECT_NE(EGL_NO_CONTEXT, eglContext);
+        EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+        if (eglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, eglContext);
+        }
+    }
+}
+
 // Emulate what a native application would do to create a
 // 10:10:10:2 surface.
 TEST_F(EGLTest, EGLConfig1010102) {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 6093c91..6ef4a3d 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -137,19 +137,18 @@
     ui::LogicalDisplayId defaultPointerDisplayId;
 
     // The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
-    //
-    // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
     int32_t mousePointerSpeed;
 
     // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
-    //
-    // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
     std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
 
-    // Velocity control parameters for mouse pointer movements.
+    // Velocity control parameters for touchpad pointer movements on the old touchpad stack (based
+    // on TouchInputMapper).
     //
-    // If the enable_new_mouse_pointer_ballistics flag is enabled, these are ignored and the values
-    // of mousePointerSpeed and mousePointerAccelerationEnabled used instead.
+    // For mice, these are ignored and the values of mousePointerSpeed and
+    // mousePointerAccelerationEnabled used instead.
+    //
+    // TODO(b/281840344): remove this.
     VelocityControlParameters pointerVelocityControlParameters;
 
     // Velocity control parameters for mouse wheel movements.
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 8dec9e5..35ba48f 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -43,6 +43,7 @@
 #include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <ftl/enum.h>
+#include <input/InputEventLabels.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
 #include <input/PrintTools.h>
@@ -1021,6 +1022,8 @@
     std::scoped_lock _l(mLock);
     const Device* device = getDeviceLocked(deviceId);
     if (device == nullptr) {
+        ALOGE("Couldn't find device with ID %d, so returning null axis info for axis %s", deviceId,
+              InputEventLookup::getLinuxEvdevLabel(EV_ABS, axis, 0).code.c_str());
         return std::nullopt;
     }
     // We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 630bd9b..b33659c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -33,8 +33,6 @@
 
 #include "input/PrintTools.h"
 
-namespace input_flags = com::android::input::flags;
-
 namespace android {
 
 // The default velocity control parameters that has no effect.
@@ -77,8 +75,7 @@
 CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
                                      const InputReaderConfiguration& readerConfig)
       : InputMapper(deviceContext, readerConfig),
-        mLastEventTime(std::numeric_limits<nsecs_t>::min()),
-        mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {}
+        mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
 
 uint32_t CursorInputMapper::getSources() const {
     return mSource;
@@ -207,8 +204,7 @@
     mDownTime = 0;
     mLastEventTime = std::numeric_limits<nsecs_t>::min();
 
-    mOldPointerVelocityControl.reset();
-    mNewPointerVelocityControl.reset();
+    mPointerVelocityControl.reset();
     mWheelXVelocityControl.reset();
     mWheelYVelocityControl.reset();
 
@@ -291,11 +287,7 @@
     mWheelYVelocityControl.move(when, nullptr, &vscroll);
     mWheelXVelocityControl.move(when, &hscroll, nullptr);
 
-    if (mEnableNewMousePointerBallistics) {
-        mNewPointerVelocityControl.move(when, &deltaX, &deltaY);
-    } else {
-        mOldPointerVelocityControl.move(when, &deltaX, &deltaY);
-    }
+    mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
@@ -486,27 +478,15 @@
 void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfiguration& config) {
     if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
         // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
-        if (mEnableNewMousePointerBallistics) {
-            mNewPointerVelocityControl.setAccelerationEnabled(false);
-        } else {
-            mOldPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
-        }
+        mPointerVelocityControl.setAccelerationEnabled(false);
         mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
         mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
     } else {
-        if (mEnableNewMousePointerBallistics) {
-            mNewPointerVelocityControl.setAccelerationEnabled(
-                    config.displaysWithMousePointerAccelerationDisabled.count(
-                            mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
-            mNewPointerVelocityControl.setCurve(
-                    createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
-        } else {
-            mOldPointerVelocityControl.setParameters(
-                    (config.displaysWithMousePointerAccelerationDisabled.count(
-                             mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0)
-                            ? config.pointerVelocityControlParameters
-                            : FLAT_VELOCITY_CONTROL_PARAMS);
-        }
+        mPointerVelocityControl.setAccelerationEnabled(
+                config.displaysWithMousePointerAccelerationDisabled.count(
+                        mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
+        mPointerVelocityControl.setCurve(
+                createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
         mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
         mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
     }
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 403e96d..8319922 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -104,8 +104,7 @@
 
     // Velocity controls for mouse pointer and wheel movements.
     // The controls for X and Y wheel movements are separate to keep them decoupled.
-    SimpleVelocityControl mOldPointerVelocityControl;
-    CurvedVelocityControl mNewPointerVelocityControl;
+    CurvedVelocityControl mPointerVelocityControl;
     SimpleVelocityControl mWheelXVelocityControl;
     SimpleVelocityControl mWheelYVelocityControl;
 
@@ -120,7 +119,6 @@
     nsecs_t mDownTime;
     nsecs_t mLastEventTime;
 
-    const bool mEnableNewMousePointerBallistics;
     bool mMouseReverseVerticalScrolling = false;
 
     explicit CursorInputMapper(InputDeviceContext& deviceContext,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index a433a72..fe3e4c2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -344,12 +344,14 @@
     }
 
     KeyboardType keyboardType = getDeviceContext().getKeyboardType();
-    // Any key down on an external keyboard should wake the device.
-    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
+    // Any key down on an external keyboard or internal alphanumeric keyboard should wake the
+    // device. We don't do this for non-alphanumeric internal keyboards to prevent them from
+    // waking up in your pocket.
     // For internal keyboards and devices for which the default wake behavior is explicitly
     // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
     // wake key individually.
-    if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
+    if (down && !mParameters.doNotWakeByDefault &&
+        (getDeviceContext().isExternal() || wakeOnAlphabeticKeyboard(keyboardType)) &&
         !(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
@@ -507,4 +509,8 @@
     return deviceSources & ALL_KEYBOARD_SOURCES;
 }
 
+bool KeyboardInputMapper::wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const {
+    return mEnableAlphabeticKeyboardWakeFlag && (KeyboardType::ALPHABETIC == keyboardType);
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 10bd424..7d9b3e4 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <com_android_input_flags.h>
+
 #include "HidUsageAccumulator.h"
 #include "InputMapper.h"
 
@@ -85,6 +87,10 @@
         bool doNotWakeByDefault{};
     } mParameters{};
 
+    // Store the value of enable wake for alphanumeric keyboard flag.
+    const bool mEnableAlphabeticKeyboardWakeFlag =
+            com::android::input::flags::enable_alphabetic_keyboard_wake();
+
     KeyboardInputMapper(InputDeviceContext& deviceContext,
                         const InputReaderConfiguration& readerConfig, uint32_t source);
     void configureParameters();
@@ -109,6 +115,8 @@
     [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
     void onKeyDownProcessed(nsecs_t downTime);
     uint32_t getEventSource() const;
+
+    bool wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const;
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 1762a45..d4e8fdf 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -25,8 +25,10 @@
 #include <android-base/logging.h>
 #include <android_companion_virtualdevice_flags.h>
 #include <com_android_input_flags.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/DisplayViewport.h>
+#include <input/InputEventLabels.h>
 #include <linux/input-event-codes.h>
 #include <linux/input.h>
 #include <utils/Timers.h>
@@ -52,6 +54,8 @@
 constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
 constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
 constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+constexpr auto AXIS_X = AMOTION_EVENT_AXIS_X;
+constexpr auto AXIS_Y = AMOTION_EVENT_AXIS_Y;
 constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
 constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
 constexpr int32_t DISPLAY_WIDTH = 480;
@@ -94,9 +98,35 @@
     return v;
 }
 
+// In a number of these tests, we want to check that some pointer motion is reported without
+// specifying an exact value, as that would require updating the tests every time the pointer
+// ballistics was changed. To do this, we make some matchers that only check the sign of a
+// particular axis.
+MATCHER_P(WithPositiveAxis, axis, "MotionEvent with a positive axis value") {
+    *result_listener << "expected 1 pointer with a positive "
+                     << InputEventLookup::getAxisLabel(axis) << " axis but got "
+                     << arg.pointerCoords.size() << " pointers, with axis value "
+                     << arg.pointerCoords[0].getAxisValue(axis);
+    return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) > 0;
+}
+
+MATCHER_P(WithZeroAxis, axis, "MotionEvent with a zero axis value") {
+    *result_listener << "expected 1 pointer with a zero " << InputEventLookup::getAxisLabel(axis)
+                     << " axis but got " << arg.pointerCoords.size()
+                     << " pointers, with axis value " << arg.pointerCoords[0].getAxisValue(axis);
+    return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) == 0;
+}
+
+MATCHER_P(WithNegativeAxis, axis, "MotionEvent with a negative axis value") {
+    *result_listener << "expected 1 pointer with a negative "
+                     << InputEventLookup::getAxisLabel(axis) << " axis but got "
+                     << arg.pointerCoords.size() << " pointers, with axis value "
+                     << arg.pointerCoords[0].getAxisValue(axis);
+    return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) < 0;
+}
+
 } // namespace
 
-namespace input_flags = com::android::input::flags;
 namespace vd_flags = android::companion::virtualdevice::flags;
 
 /**
@@ -150,24 +180,21 @@
         ASSERT_GT(mDevice->getGeneration(), generation);
     }
 
-    void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX,
-                            int32_t rotatedY) {
+    void testRotation(int32_t originalX, int32_t originalY,
+                      const testing::Matcher<NotifyMotionArgs>& coordsMatcher) {
         std::list<NotifyArgs> args;
         args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX);
         args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY);
         args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
         ASSERT_THAT(args,
                     ElementsAre(VariantWith<NotifyMotionArgs>(
-                            AllOf(WithMotionAction(ACTION_MOVE),
-                                  WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
-                                             float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD)))));
+                            AllOf(WithMotionAction(ACTION_MOVE), coordsMatcher))));
     }
 };
 
 class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
 protected:
     void SetUp() override {
-        input_flags::enable_new_mouse_pointer_ballistics(false);
         vd_flags::high_resolution_scroll(false);
         CursorInputMapperUnitTestBase::SetUp();
     }
@@ -344,14 +371,12 @@
     args += process(EV_KEY, BTN_MOUSE, 0);
     args += process(EV_SYN, SYN_REPORT, 0);
     ASSERT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(BUTTON_RELEASE),
-                                          WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(ACTION_UP),
-                                          WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+                                                         WithCoords(0.0f, 0.0f),
+                                                         WithPressure(0.0f)))));
 
     // Another move.
     args.clear();
@@ -377,7 +402,8 @@
                 ElementsAre(VariantWith<NotifyMotionArgs>(
                         AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
                               expectedCoords, expectedCursorPosition,
-                              WithRelativeMotion(10.0f, 20.0f)))));
+                              WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_X),
+                              WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_Y)))));
 }
 
 TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) {
@@ -411,64 +437,40 @@
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
-                                          WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
-                                          WithEdgeFlags(0), WithPolicyFlags(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                          WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
-                                          WithPointerCount(1), WithPointerId(0, 0),
-                                          WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
-                                          WithPressure(1.0f),
-                                          WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
-                                                        TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithDownTime(ARBITRARY_TIME))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
-                                          WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
-                                          WithEdgeFlags(0), WithPolicyFlags(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
-                                          WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
-                                          WithPointerCount(1), WithPointerId(0, 0),
-                                          WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
-                                          WithPressure(1.0f),
-                                          WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
-                                                        TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithDownTime(ARBITRARY_TIME)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+                              WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0),
+                              WithPolicyFlags(0),
+                              WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+                              WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPointerCount(1),
+                              WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+                              WithCoords(0.0f, 0.0f), WithPressure(1.0f),
+                              WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+                                            TRACKBALL_MOVEMENT_THRESHOLD),
+                              WithDownTime(ARBITRARY_TIME)))));
     args.clear();
 
     // Button release.  Should have same down time.
     args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
     args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithEventTime(ARBITRARY_TIME + 1),
-                                          WithDeviceId(DEVICE_ID),
-                                          WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
-                                          WithEdgeFlags(0), WithPolicyFlags(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
-                                          WithButtonState(0), WithPointerCount(1),
-                                          WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f),
-                                          WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
-                                                        TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithDownTime(ARBITRARY_TIME))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithEventTime(ARBITRARY_TIME + 1),
-                                          WithDeviceId(DEVICE_ID),
-                                          WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
-                                          WithEdgeFlags(0), WithPolicyFlags(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_UP),
-                                          WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
-                                          WithButtonState(0), WithPointerCount(1),
-                                          WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f),
-                                          WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
-                                                        TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithDownTime(ARBITRARY_TIME)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithEventTime(ARBITRARY_TIME + 1), WithDeviceId(DEVICE_ID),
+                              WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0),
+                              WithPolicyFlags(0),
+                              WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+                              WithButtonState(0), WithPointerCount(1), WithPointerId(0, 0),
+                              WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+                              WithPressure(0.0f),
+                              WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+                                            TRACKBALL_MOVEMENT_THRESHOLD),
+                              WithDownTime(ARBITRARY_TIME)))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) {
@@ -482,9 +484,8 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
-                              WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f),
-                              WithPressure(0.0f)))));
+                        AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f),
+                              WithPositiveAxis(AXIS_X), WithZeroAxis(AXIS_Y)))));
     args.clear();
 
     // Motion in Y but not X.
@@ -492,9 +493,8 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
-                              WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
-                              WithPressure(0.0f)))));
+                        AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f),
+                              WithZeroAxis(AXIS_X), WithNegativeAxis(AXIS_Y)))));
     args.clear();
 }
 
@@ -508,24 +508,22 @@
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
     args.clear();
 
     // Button release.
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) {
@@ -540,16 +538,12 @@
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                          WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
-                                                     -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithPressure(1.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
-                                          WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
-                                                     -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
-                                          WithPressure(1.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(AllOf(WithPositiveAxis(AXIS_X),
+                                                         WithNegativeAxis(AXIS_Y),
+                                                         WithPressure(1.0f)))));
     args.clear();
 
     // Move X, Y a bit while pressed.
@@ -558,22 +552,19 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
-                              WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
-                                         1.0f / TRACKBALL_MOVEMENT_THRESHOLD),
-                              WithPressure(1.0f)))));
+                        AllOf(WithMotionAction(ACTION_MOVE), WithPressure(1.0f),
+                              WithPositiveAxis(AXIS_X), WithPositiveAxis(AXIS_Y)))));
     args.clear();
 
     // Release Button.
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
     args.clear();
 }
 
@@ -586,14 +577,16 @@
             .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
     mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
 
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0,  1,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  0,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  0, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  1, -1,  1));
+    constexpr auto X = AXIS_X;
+    constexpr auto Y = AXIS_Y;
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0,  1, AllOf(WithZeroAxis(X),     WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X),     WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) {
@@ -604,54 +597,56 @@
             .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0)));
     mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
 
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0,  1,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  0,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  0, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  1, -1,  1));
+    constexpr auto X = AXIS_X;
+    constexpr auto Y = AXIS_Y;
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0,  1, AllOf(WithZeroAxis(X),     WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X),     WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
 
     EXPECT_CALL((*mDevice), getAssociatedViewport)
             .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
     std::list<NotifyArgs> args =
             mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
                                  InputReaderConfiguration::Change::DISPLAY_INFO);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0,  1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  1, -1, -1));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0,  1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  0, AllOf(WithZeroAxis(X),     WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  0, AllOf(WithZeroAxis(X),     WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
 
     EXPECT_CALL((*mDevice), getAssociatedViewport)
             .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180)));
     args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
                                 InputReaderConfiguration::Change::DISPLAY_INFO);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0,  1,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  0, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  0,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  1,  1, -1));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0,  1, AllOf(WithZeroAxis(X),     WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X),     WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
 
     EXPECT_CALL((*mDevice), getAssociatedViewport)
             .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270)));
     args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
                                 InputReaderConfiguration::Change::DISPLAY_INFO);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0,  1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1,  1,  1,  1));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0,  1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1,  0, AllOf(WithZeroAxis(X),     WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  0, AllOf(WithZeroAxis(X),     WithPositiveAxis(Y))));
+    ASSERT_NO_FATAL_FAILURE(testRotation(-1,  1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
 }
 
 TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsRangeFromPolicy) {
@@ -742,30 +737,22 @@
     args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0.0f, 0.0f),
+                              WithPressure(1.0f)))));
     args.clear();
     args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
     args.clear();
 
     // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
@@ -774,49 +761,41 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                                    AllOf(WithMotionAction(ACTION_DOWN),
                                           WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
-                                                          AMOTION_EVENT_BUTTON_TERTIARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+                                                          AMOTION_EVENT_BUTTON_TERTIARY))),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
+                                          WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY))),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
                                           WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
-                                                          AMOTION_EVENT_BUTTON_TERTIARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+                                                          AMOTION_EVENT_BUTTON_TERTIARY)))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
     args.clear();
 
     args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
-                                          WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
-                                          WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_MOVE))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+                              WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
     args.clear();
 
     args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithButtonState(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_UP),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithButtonState(0),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+    EXPECT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
 }
 
 class CursorInputMapperButtonKeyTest
@@ -838,11 +817,11 @@
                 ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
                                                              WithKeyCode(expectedKeyCode))),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                                    AllOf(WithMotionAction(HOVER_MOVE),
                                           WithButtonState(expectedButtonState),
                                           WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                                    AllOf(WithMotionAction(BUTTON_PRESS),
                                           WithButtonState(expectedButtonState),
                                           WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
     args.clear();
@@ -851,13 +830,11 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f))),
+                                    AllOf(WithMotionAction(BUTTON_RELEASE), WithButtonState(0),
+                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                                          WithButtonState(0), WithCoords(0.0f, 0.0f),
-                                          WithPressure(0.0f))),
+                                    AllOf(WithMotionAction(HOVER_MOVE), WithButtonState(0),
+                                          WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
                             VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
                                                              WithKeyCode(expectedKeyCode)))));
 }
@@ -881,8 +858,7 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                              WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                        AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE),
                               WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f),
                               WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
                               WithOrientation(0.0f), WithDistance(0.0f)))));
@@ -897,13 +873,11 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
                                           WithScroll(1.0f, 1.0f)))));
+    EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) {
@@ -920,13 +894,11 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
                                           WithScroll(0.5f, 0.5f)))));
+    EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
 }
 
 TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) {
@@ -945,13 +917,11 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
                                           WithScroll(0.5f, 0.5f)))));
+    EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) {
@@ -966,13 +936,11 @@
     // Reversed vertical scrolling only affects the y-axis, expect it to be -1.0f to indicate the
     // inverted scroll direction.
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
                                           WithScroll(1.0f, -1.0f)))));
+    EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) {
@@ -990,13 +958,11 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+                ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
                             VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
                                           WithScroll(0.5f, -0.5f)))));
+    EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
 }
 
 /**
@@ -1005,10 +971,6 @@
  */
 TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
     mPropertyMap.addProperty("cursor.mode", "pointer");
-    const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
-                                               /*highThreshold=*/100.f, /*acceleration=*/10.f);
-    mReaderConfiguration.pointerVelocityControlParameters = testParams;
-    mFakePolicy->setVelocityControlParams(testParams);
     createMapper();
 
     NotifyMotionArgs motionArgs;
@@ -1020,8 +982,7 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                              WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+                        AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE)))));
     motionArgs = std::get<NotifyMotionArgs>(args.front());
     const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
     const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
@@ -1039,12 +1000,7 @@
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
                         AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
-                              WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))));
-    motionArgs = std::get<NotifyMotionArgs>(args.front());
-    const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-    const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-    ASSERT_EQ(10, relX2);
-    ASSERT_EQ(20, relY2);
+                              WithMotionAction(ACTION_MOVE), WithRelativeMotion(10, 20)))));
 }
 
 TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) {
@@ -1067,54 +1023,12 @@
     args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(args,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                              WithSource(AINPUT_SOURCE_MOUSE),
+                        AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
                               WithDisplayId(ui::LogicalDisplayId::INVALID),
                               WithCoords(0.0f, 0.0f)))));
 }
 
-// TODO(b/320433834): De-duplicate the test cases once the flag is removed.
-class CursorInputMapperUnitTestWithNewBallistics : public CursorInputMapperUnitTestBase {
-protected:
-    void SetUp() override {
-        input_flags::enable_new_mouse_pointer_ballistics(true);
-        CursorInputMapperUnitTestBase::SetUp();
-    }
-};
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) {
-    mPropertyMap.addProperty("cursor.mode", "pointer");
-    createMapper();
-
-    NotifyMotionArgs motionArgs;
-    std::list<NotifyArgs> args;
-
-    // Move and verify scale is applied.
-    args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
-    args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-    motionArgs = std::get<NotifyMotionArgs>(args.front());
-    const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-    const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-    ASSERT_GT(relX, 10);
-    ASSERT_GT(relY, 20);
-    args.clear();
-
-    // Enable Pointer Capture
-    setPointerCapture(true);
-
-    // Move and verify scale is not applied.
-    args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
-    args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-    motionArgs = std::get<NotifyMotionArgs>(args.front());
-    const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-    const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-    ASSERT_EQ(10, relX2);
-    ASSERT_EQ(20, relY2);
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) {
+TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) {
     mPropertyMap.addProperty("cursor.mode", "pointer");
     DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
     mReaderConfiguration.setDisplayViewports({primaryViewport});
@@ -1149,7 +1063,7 @@
                                                                 WithRelativeMotion(10, 20)))));
 }
 
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDisplayChange) {
+TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationOnDisplayChange) {
     mPropertyMap.addProperty("cursor.mode", "pointer");
     DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
     mReaderConfiguration.setDisplayViewports({primaryViewport});
@@ -1186,72 +1100,6 @@
                               WithRelativeMotion(10, 20)))));
 }
 
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) {
-    createMapper();
-
-    std::list<NotifyArgs> args;
-    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
-    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
-    EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
-                                          WithScroll(1.0f, 1.0f)))));
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) {
-    vd_flags::high_resolution_scroll(true);
-    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
-            .WillRepeatedly(Return(true));
-    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
-            .WillRepeatedly(Return(true));
-    createMapper();
-
-    std::list<NotifyArgs> args;
-    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
-    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
-    EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
-                                          WithScroll(0.5f, 0.5f)))));
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) {
-    vd_flags::high_resolution_scroll(true);
-    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
-            .WillRepeatedly(Return(true));
-    EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
-            .WillRepeatedly(Return(true));
-    createMapper();
-
-    std::list<NotifyArgs> args;
-    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
-    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
-    args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
-    args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
-    args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
-    EXPECT_THAT(args,
-                ElementsAre(VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
-                            VariantWith<NotifyMotionArgs>(
-                                    AllOf(WithSource(AINPUT_SOURCE_MOUSE),
-                                          WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
-                                          WithScroll(0.5f, 0.5f)))));
-}
-
 namespace {
 
 // Minimum timestamp separation between subsequent input events from a Bluetooth device.
@@ -1279,8 +1127,7 @@
     argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(argsList,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                              WithEventTime(expectedEventTime)))));
+                        AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
     argsList.clear();
 
     // Process several events that come in quick succession, according to their timestamps.
@@ -1294,7 +1141,7 @@
         argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
         EXPECT_THAT(argsList,
                     ElementsAre(VariantWith<NotifyMotionArgs>(
-                            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                            AllOf(WithMotionAction(HOVER_MOVE),
                                   WithEventTime(expectedEventTime)))));
         argsList.clear();
     }
@@ -1310,8 +1157,7 @@
     argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(argsList,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                              WithEventTime(expectedEventTime)))));
+                        AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
     argsList.clear();
 
     // Process several events with the same timestamp from the kernel.
@@ -1325,7 +1171,7 @@
         argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
         EXPECT_THAT(argsList,
                     ElementsAre(VariantWith<NotifyMotionArgs>(
-                            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                            AllOf(WithMotionAction(HOVER_MOVE),
                                   WithEventTime(expectedEventTime)))));
         argsList.clear();
     }
@@ -1338,8 +1184,7 @@
         argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
         EXPECT_THAT(argsList,
                     ElementsAre(VariantWith<NotifyMotionArgs>(
-                            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                                  WithEventTime(cappedEventTime)))));
+                            AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(cappedEventTime)))));
         argsList.clear();
     }
 }
@@ -1355,8 +1200,7 @@
     argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(argsList,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                              WithEventTime(expectedEventTime)))));
+                        AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
     argsList.clear();
 
     // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
@@ -1368,8 +1212,7 @@
     argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
     EXPECT_THAT(argsList,
                 ElementsAre(VariantWith<NotifyMotionArgs>(
-                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
-                              WithEventTime(expectedEventTime)))));
+                        AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
     argsList.clear();
 }
 
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index f373cac..7c5f350 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -217,7 +217,6 @@
 }
 
 void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) {
-    mConfig.pointerVelocityControlParameters = params;
     mConfig.wheelVelocityControlParameters = params;
 }
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8b75a33..ee3b2a2 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -35,6 +35,7 @@
 #include <UinputDevice.h>
 #include <android-base/thread_annotations.h>
 #include <com_android_input_flags.h>
+#include <flag_macros.h>
 #include <ftl/enum.h>
 #include <gtest/gtest.h>
 #include <ui/Rotation.h>
@@ -3865,6 +3866,44 @@
     ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
 }
 
+TEST_F_WITH_FLAGS(KeyboardInputMapperTest, WakeBehavior_AlphabeticKeyboard,
+                  REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+                                                      enable_alphabetic_keyboard_wake))) {
+    // For internal alphabetic devices, keys will trigger wake on key down.
+
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, 0);
+
+    KeyboardInputMapper& mapper =
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+}
+
 /**
  * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
  * events that use the shared keyboard source across all mappers. This is to ensure that each
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 1589c99..f5c4fc5 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -35,6 +35,7 @@
 using aidl::android::hardware::power::Mode;
 using aidl::android::hardware::power::SessionConfig;
 using aidl::android::hardware::power::SessionTag;
+using aidl::android::hardware::power::SupportInfo;
 using android::binder::Status;
 
 using namespace android;
@@ -65,6 +66,7 @@
                 (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
     MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
     MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index d500ae8..f1c79c1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -148,6 +148,46 @@
     },
 }
 
+// libsurfaceflinger_backend_{headers|sources} are a step towards pulling out
+// the "backend" sources to clean up the dependency graph between
+// CompositionEngine and SurfaceFlinger. Completing the cleanup would require
+// moving the headers in particular so that the dependency can strictly be a
+// DAG. There would certainly be additional cleanups: VirtualDisplaySurface.cpp
+// and FrameBufferSurface.cpp likely belong in CompositionEngine for example.
+cc_library_headers {
+    name: "libsurfaceflinger_backend_headers",
+    export_include_dirs: ["."],
+    static_libs: ["libserviceutils"],
+    export_static_lib_headers: ["libserviceutils"],
+
+    shared_libs: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "libbinder_ndk",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "libbinder_ndk",
+    ],
+}
+
+filegroup {
+    name: "libsurfaceflinger_backend_sources",
+    srcs: [
+        "DisplayHardware/AidlComposerHal.cpp",
+        "DisplayHardware/ComposerHal.cpp",
+        "DisplayHardware/FramebufferSurface.cpp",
+        "DisplayHardware/HWC2.cpp",
+        "DisplayHardware/HWComposer.cpp",
+        "DisplayHardware/HidlComposerHal.cpp",
+        "DisplayHardware/PowerAdvisor.cpp",
+        "DisplayHardware/VirtualDisplaySurface.cpp",
+    ],
+}
+
 cc_library_headers {
     name: "libsurfaceflinger_headers",
     export_include_dirs: ["."],
@@ -158,20 +198,13 @@
 filegroup {
     name: "libsurfaceflinger_sources",
     srcs: [
+        ":libsurfaceflinger_backend_sources",
         "BackgroundExecutor.cpp",
         "Client.cpp",
         "ClientCache.cpp",
         "Display/DisplayModeController.cpp",
         "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
-        "DisplayHardware/AidlComposerHal.cpp",
-        "DisplayHardware/ComposerHal.cpp",
-        "DisplayHardware/FramebufferSurface.cpp",
-        "DisplayHardware/HWC2.cpp",
-        "DisplayHardware/HWComposer.cpp",
-        "DisplayHardware/HidlComposerHal.cpp",
-        "DisplayHardware/PowerAdvisor.cpp",
-        "DisplayHardware/VirtualDisplaySurface.cpp",
         "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
         "FrontEnd/LayerCreationArgs.cpp",
@@ -251,7 +284,6 @@
     ],
     static_libs: [
         "android.frameworks.displayservice@1.0",
-        "libc++fs",
         "libdisplayservicehidl",
         "libserviceutils",
     ],
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b4ac9ba..7095b9d 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -59,7 +59,7 @@
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
         "android.hardware.graphics.composer3-command-buffer",
-        "libsurfaceflinger_headers",
+        "libsurfaceflinger_backend_headers",
     ],
 }
 
@@ -141,6 +141,8 @@
     ],
     srcs: [
         ":libcompositionengine_sources",
+        ":libsurfaceflinger_backend_mock_sources",
+        ":libsurfaceflinger_backend_sources",
         "tests/planner/CachedSetTest.cpp",
         "tests/planner/FlattenerTest.cpp",
         "tests/planner/LayerStateTest.cpp",
@@ -151,14 +153,14 @@
         "tests/DisplayTest.cpp",
         "tests/HwcAsyncWorkerTest.cpp",
         "tests/HwcBufferCacheTest.cpp",
-        "tests/MockHWC2.cpp",
-        "tests/MockHWComposer.cpp",
-        "tests/MockPowerAdvisor.cpp",
         "tests/OutputLayerTest.cpp",
         "tests/OutputTest.cpp",
         "tests/ProjectionSpaceTest.cpp",
         "tests/RenderSurfaceTest.cpp",
     ],
+    header_libs: [
+        "libsurfaceflinger_backend_mock_headers",
+    ],
     static_libs: [
         "libcompositionengine_mocks",
         "libgui_mocks",
@@ -167,6 +169,7 @@
         "libgtest",
     ],
     shared_libs: [
+        "libbinder_ndk",
         // For some reason, libvulkan isn't picked up from librenderengine
         // Probably ASAN related?
         "libvulkan",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 14a8fd6..8a91a07 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 
 #include <android/gui/CachingHint.h>
+#include <gui/DisplayLuts.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/BlurRegion.h>
@@ -219,6 +220,9 @@
     float desiredHdrSdrRatio = 1.f;
 
     gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+
+    std::shared_ptr<gui::DisplayLuts> luts;
+
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 191d475..556aa24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -329,6 +329,8 @@
     virtual bool isPowerHintSessionGpuReportingEnabled() = 0;
     virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
     virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
+    virtual const aidl::android::hardware::graphics::composer3::OverlayProperties*
+    getOverlaySupport() = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index dcfe21a..80c5124 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -93,7 +93,10 @@
     // transform, if needed.
     virtual void updateCompositionState(
             bool includeGeometry, bool forceClientComposition,
-            ui::Transform::RotationFlags internalDisplayRotationFlags) = 0;
+            ui::Transform::RotationFlags internalDisplayRotationFlags,
+            const std::optional<std::vector<
+                    std::optional<aidl::android::hardware::graphics::composer3::LutProperties>>>
+                    properties) = 0;
 
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
@@ -129,8 +132,10 @@
     virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0;
 
     // Applies a HWC device layer lut
-    virtual void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties,
-                                     ndk::ScopedFileDescriptor) = 0;
+    virtual void applyDeviceLayerLut(
+            ndk::ScopedFileDescriptor,
+            std::vector<std::pair<
+                    int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0;
 
     // Returns true if the composition settings scale pixels
     virtual bool needsFiltering() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index a39abb4..d8466ff 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -103,6 +103,8 @@
     DisplayId mId;
     bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
+            override;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 9990a74..69e1efc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -164,6 +164,8 @@
     bool mustRecompose() const;
 
     const std::string& getNamePlusId() const { return mNamePlusId; }
+    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
+            override;
 
 private:
     void dirtyEntireOutput();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 354a441..0c7e4dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -31,6 +31,8 @@
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
 namespace android::compositionengine {
 
 struct LayerFECompositionState;
@@ -48,7 +50,9 @@
     void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
 
     void updateCompositionState(bool includeGeometry, bool forceClientComposition,
-                                ui::Transform::RotationFlags) override;
+                                ui::Transform::RotationFlags,
+                                const std::optional<std::vector<std::optional<LutProperties>>>
+                                        properties = std::nullopt) override;
     void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
                          bool isPeekingThrough) override;
     void writeCursorPositionToHWC() const override;
@@ -60,8 +64,8 @@
             aidl::android::hardware::graphics::composer3::Composition) override;
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
-    void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties,
-                             ndk::ScopedFileDescriptor) override;
+    void applyDeviceLayerLut(ndk::ScopedFileDescriptor,
+                             std::vector<std::pair<int, LutProperties>>) override;
     bool needsFiltering() const override;
     std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
 
@@ -92,10 +96,13 @@
     void writeCompositionTypeToHWC(HWC2::Layer*,
                                    aidl::android::hardware::graphics::composer3::Composition,
                                    bool isPeekingThrough, bool skipLayer);
+    void writeLutToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void detectDisallowedCompositionTypeChange(
             aidl::android::hardware::graphics::composer3::Composition from,
             aidl::android::hardware::graphics::composer3::Composition to) const;
     bool isClientCompositionForced(bool isPeekingThrough) const;
+    void updateLuts(std::shared_ptr<gui::DisplayLuts> luts,
+                    const std::optional<std::vector<std::optional<LutProperties>>>& properties);
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 6c419da..28216a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -18,6 +18,7 @@
 
 #include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/DisplayLuts.h>
 #include <renderengine/ExternalTexture.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicTypes.h>
@@ -151,6 +152,9 @@
 
         // True when this layer was skipped as part of SF-side layer caching.
         bool layerSkipped = false;
+
+        // lut information
+        std::shared_ptr<gui::DisplayLuts> luts;
     };
 
     // The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d5bf2b5..33cdc54 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -140,6 +140,8 @@
     MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine));
     MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
     MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ());
+    MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*),
+                getOverlaySupport, ());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 48c2f9c..12f2094 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -43,7 +43,10 @@
     MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
-    MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
+    MOCK_METHOD(void, updateCompositionState,
+                (bool, bool, ui::Transform::RotationFlags,
+                 (const std::optional<std::vector<std::optional<
+                          aidl::android::hardware::graphics::composer3::LutProperties>>>)));
     MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
     MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
 
@@ -57,8 +60,9 @@
     MOCK_CONST_METHOD0(needsFiltering, bool());
     MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
     MOCK_METHOD(void, applyDeviceLayerLut,
-                (aidl::android::hardware::graphics::composer3::LutProperties,
-                 ndk::ScopedFileDescriptor));
+                (ndk::ScopedFileDescriptor,
+                 (std::vector<std::pair<
+                          int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index b0164b7..1825065 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -370,8 +370,8 @@
 
         if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
             if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
-                layer->applyDeviceLayerLut(lutsIt->second,
-                                           ndk::ScopedFileDescriptor(mapperIt->second.release()));
+                layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()),
+                                           lutsIt->second);
             }
         }
     }
@@ -457,6 +457,11 @@
     mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
 }
 
+const aidl::android::hardware::graphics::composer3::OverlayProperties*
+Display::getOverlaySupport() {
+    return &getCompositionEngine().getHwComposer().getOverlaySupport();
+}
+
 void Display::finishFrame(GpuCompositionResult&& result) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 22ab3d9..bb45613 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -813,11 +813,14 @@
     mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();
     bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;
 
+    auto* properties = getOverlaySupport();
+
     for (auto* layer : getOutputLayersOrderedByZ()) {
         layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
                                       refreshArgs.devOptForceClientComposition ||
                                               forceClientComposition,
-                                      refreshArgs.internalDisplayRotationFlags);
+                                      refreshArgs.internalDisplayRotationFlags,
+                                      properties ? properties->lutProperties : std::nullopt);
 
         if (mLayerRequestingBackgroundBlur == layer) {
             forceClientComposition = false;
@@ -1678,6 +1681,10 @@
     editState().treat170mAsSrgb = enable;
 }
 
+const aidl::android::hardware::graphics::composer3::OverlayProperties* Output::getOverlaySupport() {
+    return nullptr;
+}
+
 bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
     uint64_t lastOutputLayerHash = getState().lastOutputLayerHash;
     uint64_t outputLayerHash = getState().outputLayerHash;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 2d46dc0..934909d 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -38,7 +38,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
 using aidl::android::hardware::graphics::composer3::Composition;
-using aidl::android::hardware::graphics::composer3::LutProperties;
+using aidl::android::hardware::graphics::composer3::Luts;
 
 namespace android::compositionengine {
 
@@ -285,9 +285,50 @@
     return transform.getOrientation();
 }
 
+void OutputLayer::updateLuts(
+        std::shared_ptr<gui::DisplayLuts> layerFEStateLut,
+        const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+    auto& state = editState();
+
+    if (!properties) {
+        // GPU composition if no Hwc Luts
+        state.forceClientComposition = true;
+        return;
+    }
+
+    std::vector<LutProperties> hwcLutProperties;
+    for (auto& p : *properties) {
+        if (p) {
+            hwcLutProperties.emplace_back(*p);
+        }
+    }
+
+    for (const auto& inputLut : layerFEStateLut->lutProperties) {
+        bool foundInHwcLuts = false;
+        for (const auto& hwcLut : hwcLutProperties) {
+            if (static_cast<int32_t>(hwcLut.dimension) ==
+                        static_cast<int32_t>(inputLut.dimension) &&
+                hwcLut.size == inputLut.size &&
+                std::find(hwcLut.samplingKeys.begin(), hwcLut.samplingKeys.end(),
+                          static_cast<LutProperties::SamplingKey>(inputLut.samplingKey)) !=
+                        hwcLut.samplingKeys.end()) {
+                foundInHwcLuts = true;
+                break;
+            }
+        }
+        // if any lut properties of layerFEStateLut can not be found in hwcLutProperties,
+        // GPU composition instead
+        if (!foundInHwcLuts) {
+            state.forceClientComposition = true;
+            return;
+        }
+    }
+}
+
 void OutputLayer::updateCompositionState(
         bool includeGeometry, bool forceClientComposition,
-        ui::Transform::RotationFlags internalDisplayRotationFlags) {
+        ui::Transform::RotationFlags internalDisplayRotationFlags,
+        const std::optional<std::vector<std::optional<LutProperties>>> properties) {
     const auto* layerFEState = getLayerFE().getCompositionState();
     if (!layerFEState) {
         return;
@@ -368,6 +409,11 @@
         state.whitePointNits = layerBrightnessNits;
     }
 
+    const auto& layerFEStateLut = layerFEState->luts;
+    if (layerFEStateLut) {
+        updateLuts(layerFEStateLut, properties);
+    }
+
     // These are evaluated every frame as they can potentially change at any
     // time.
     if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -420,6 +466,8 @@
     writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
                               skipLayer);
 
+    writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+
     if (requestedCompositionType == Composition::SOLID_COLOR) {
         writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
     }
@@ -513,6 +561,40 @@
     }
 }
 
+void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer,
+                                const LayerFECompositionState& outputIndependentState) {
+    if (!outputIndependentState.luts) {
+        return;
+    }
+    auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
+    auto lutOffsets = outputIndependentState.luts->offsets;
+    auto& lutProperties = outputIndependentState.luts->lutProperties;
+
+    std::vector<LutProperties> aidlProperties;
+    aidlProperties.reserve(lutProperties.size());
+    for (size_t i = 0; i < lutOffsets.size(); i++) {
+        LutProperties properties;
+        properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
+        properties.size = lutProperties[i].size;
+        properties.samplingKeys = {
+                static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
+        aidlProperties.emplace_back(properties);
+    }
+
+    Luts luts;
+    luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+    luts.offsets = lutOffsets;
+    luts.lutProperties = std::move(aidlProperties);
+
+    switch (auto error = hwcLayer->setLuts(luts)) {
+        case hal::Error::NONE:
+            break;
+        default:
+            ALOGE("[%s] Failed to set Luts: %s (%d)", getLayerFE().getDebugName(),
+                  to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
 void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
     const auto& outputDependentState = getState();
 
@@ -849,10 +931,29 @@
     }
 }
 
-void OutputLayer::applyDeviceLayerLut(LutProperties /*lutProperties*/,
-                                      ndk::ScopedFileDescriptor /*lutPfd*/) {
-    // TODO(b/329472856): decode the shared memory of the pfd, and store the lut data into
-    // OutputLayerCompositionState#hwc struct
+void OutputLayer::applyDeviceLayerLut(
+        ndk::ScopedFileDescriptor lutFileDescriptor,
+        std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) {
+    auto& state = editState();
+    LOG_FATAL_IF(!state.hwc);
+    auto& hwcState = *state.hwc;
+    std::vector<int32_t> offsets;
+    std::vector<int32_t> dimensions;
+    std::vector<int32_t> sizes;
+    std::vector<int32_t> samplingKeys;
+    for (const auto& [offset, properties] : lutOffsetsAndProperties) {
+        // The Lut(s) that comes back through CommandResultPayload should be
+        // only one sampling key.
+        if (properties.samplingKeys.size() == 1) {
+            offsets.emplace_back(offset);
+            dimensions.emplace_back(static_cast<int32_t>(properties.dimension));
+            sizes.emplace_back(static_cast<int32_t>(properties.size));
+            samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0]));
+        }
+    }
+    hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()),
+                                                       std::move(offsets), std::move(dimensions),
+                                                       std::move(sizes), std::move(samplingKeys));
 }
 
 bool OutputLayer::needsFiltering() const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 48ebc32..3e0c390 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -26,9 +26,8 @@
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
-#include "MockHWComposer.h"
 #include "TimeStats/TimeStats.h"
-#include "gmock/gmock.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 using namespace com::android::graphics::surfaceflinger;
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 9c0e62c..416001e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -36,10 +36,10 @@
 #include <ui/Rect.h>
 #include <ui/StaticDisplayInfo.h>
 
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
-#include "MockPowerAdvisor.h"
 #include "ftl/future.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
deleted file mode 100644
index 0baa79d..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MockHWC2.h"
-
-namespace android::HWC2 {
-
-// This will go away once HWC2::Layer is moved into the "backend" library
-Layer::~Layer() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace mock
-} // namespace android::HWC2
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
deleted file mode 100644
index eb6e677..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-#include <ui/Fence.h>
-#include <ui/FloatRect.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/Transform.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <ui/GraphicTypes.h>
-#include "DisplayHardware/HWC2.h"
-
-#include <aidl/android/hardware/graphics/composer3/Composition.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace HWC2 {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-using Error = hal::Error;
-
-class Layer : public HWC2::Layer {
-public:
-    Layer();
-    ~Layer() override;
-
-    MOCK_CONST_METHOD0(getId, hal::HWLayerId());
-
-    MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t));
-    MOCK_METHOD3(setBuffer,
-                 Error(uint32_t, const android::sp<android::GraphicBuffer>&,
-                       const android::sp<android::Fence>&));
-    MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t));
-    MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
-    MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
-    MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
-    MOCK_METHOD1(setCompositionType,
-                 Error(aidl::android::hardware::graphics::composer3::Composition));
-    MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
-    MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
-    MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
-    MOCK_METHOD1(setPlaneAlpha, Error(float));
-    MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*));
-    MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&));
-    MOCK_METHOD1(setTransform, Error(hal::Transform));
-    MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
-    MOCK_METHOD1(setZOrder, Error(uint32_t));
-
-    MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
-    MOCK_METHOD3(setLayerGenericMetadata,
-                 Error(const std::string&, bool, const std::vector<uint8_t>&));
-    MOCK_METHOD1(setBrightness, Error(float));
-    MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
-    MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
-};
-
-} // namespace mock
-} // namespace HWC2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
deleted file mode 100644
index 5c55ce7..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <compositionengine/Output.h>
-#include <gmock/gmock.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include "DisplayHardware/HWComposer.h"
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-class HWComposer : public android::HWComposer {
-public:
-    HWComposer();
-    ~HWComposer() override;
-
-    MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&));
-    MOCK_CONST_METHOD3(getDisplayIdentificationData,
-                       bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
-    MOCK_CONST_METHOD1(hasCapability,
-                       bool(aidl::android::hardware::graphics::composer3::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability,
-                       bool(HalDisplayId,
-                            aidl::android::hardware::graphics::composer3::DisplayCapability));
-
-    MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
-    MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
-    MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
-    MOCK_METHOD3(allocatePhysicalDisplay,
-                 void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>));
-
-    MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
-    MOCK_METHOD(status_t, getDeviceCompositionChanges,
-                (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
-                 Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
-    MOCK_METHOD(status_t, setClientTarget,
-                (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
-                 float),
-                (override));
-    MOCK_METHOD2(presentAndGetReleaseFences,
-                 status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
-    MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
-    MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
-    MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
-    MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
-    MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
-    MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
-    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
-    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
-    MOCK_METHOD3(setOutputBuffer,
-                 status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
-    MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
-    MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
-    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
-    MOCK_CONST_METHOD2(getRenderIntents,
-                       std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
-    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
-    MOCK_METHOD4(getDisplayedContentSamplingAttributes,
-                 status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
-    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
-    MOCK_METHOD4(getDisplayedContentSample,
-                 status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
-                (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
-                (override));
-    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
-
-    MOCK_METHOD2(onHotplug,
-                 std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
-    MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
-    MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
-    MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
-    MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
-    MOCK_CONST_METHOD2(getModes,
-                       std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
-    MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
-    MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
-    MOCK_CONST_METHOD0(isUsingVrComposer, bool());
-    MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId));
-    MOCK_METHOD4(setActiveModeWithConstraints,
-                 status_t(PhysicalDisplayId, hal::HWConfigId,
-                          const hal::VsyncPeriodChangeConstraints&,
-                          hal::VsyncPeriodChangeTimeline*));
-    MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId));
-    MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
-    MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
-    MOCK_METHOD0(getBootDisplayModeSupport, bool());
-    MOCK_CONST_METHOD0(
-            getHdrConversionCapabilities,
-            std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>());
-    MOCK_METHOD2(setHdrConversionStrategy,
-                 status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy,
-                          aidl::android::hardware::graphics::common::Hdr*));
-    MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
-    MOCK_METHOD(status_t, getSupportedContentTypes,
-                (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
-    MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
-    MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
-                       const std::unordered_map<std::string, bool>&());
-
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-    MOCK_CONST_METHOD1(dumpOverlayProperties, void(std::string&));
-    MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-
-    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
-    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
-    MOCK_METHOD(bool, isHeadless, (), (const, override));
-
-    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
-                (const, override));
-    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
-                (const, override));
-    MOCK_METHOD2(getDisplayDecorationSupport,
-                 status_t(PhysicalDisplayId,
-                          std::optional<aidl::android::hardware::graphics::common::
-                                                DisplayDecorationSupport>* support));
-    MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds));
-    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
-    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
-                (const, override));
-    MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
-    MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
-                getOverlaySupport, (), (const, override));
-    MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
-    MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
-    MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ());
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
deleted file mode 100644
index 85b9403..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MockPowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-
-// This will go away once PowerAdvisor is moved into the "backend" library
-PowerAdvisor::~PowerAdvisor() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
deleted file mode 100644
index ed2ffa9..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayHardware/PowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-namespace mock {
-
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
-public:
-    PowerAdvisor();
-    ~PowerAdvisor() override;
-
-    MOCK_METHOD(void, init, (), (override));
-    MOCK_METHOD(void, onBootFinished, (), (override));
-    MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
-                (override));
-    MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
-    MOCK_METHOD(bool, usePowerHintSession, (), (override));
-    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
-    MOCK_METHOD(bool, supportsGpuReporting, (), (override));
-    MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
-    MOCK_METHOD(void, reportActualWorkDuration, (), (override));
-    MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
-    MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
-    MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override));
-    MOCK_METHOD(void, setGpuFenceTime,
-                (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
-    MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
-                (override));
-    MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
-                (override));
-    MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
-    MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine),
-                (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
-                (override));
-    MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId, TimePoint earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
-    MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
-    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
-};
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index b21533a..f2c5672 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -23,14 +23,14 @@
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
-
 #include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
+#include <ui/FloatRect.h>
 #include <ui/PixelFormat.h>
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
+
 #include "RegionMatcher.h"
-#include "ui/FloatRect.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1c18cd2..fe7dd9a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -40,8 +40,8 @@
 #include <common/FlagManager.h>
 #include <common/test/FlagUtils.h>
 #include "CallOrderStateMachineHelper.h"
-#include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "mock/DisplayHardware/MockHWC2.h"
 
 namespace android::compositionengine {
 namespace {
@@ -785,17 +785,20 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer1.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer2.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer3.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -822,17 +825,17 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -858,17 +861,17 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -896,11 +899,11 @@
     InjectedLayer layer3;
 
     InSequence seq;
-    EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
 
     uint32_t z = 0;
     EXPECT_CALL(*layer0.outputLayer,
@@ -4932,12 +4935,12 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition, unless opaque.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -4966,17 +4969,17 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -5006,17 +5009,17 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 77bd804..5814aa4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,7 +44,7 @@
 using aidl::android::hardware::graphics::composer3::BnComposerCallback;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::PowerMode;
 using aidl::android::hardware::graphics::composer3::VirtualDisplay;
 
@@ -1565,7 +1565,7 @@
     return error;
 }
 
-Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) {
+Error AidlComposer::setLayerLuts(Display display, Layer layer, Luts& luts) {
     Error error = Error::NONE;
     mMutex.lock_shared();
     if (auto writer = getWriter(display)) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index cdb67e4..d724b21 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -53,6 +53,7 @@
 using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::ComposerClientReader;
 using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 class AidlIComposerCallbackWrapper;
@@ -248,9 +249,7 @@
             Display display, std::vector<Layer>* outLayers,
             std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
                     outLuts) override;
-    Error setLayerLuts(
-            Display display, Layer layer,
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
+    Error setLayerLuts(Display display, Layer layer, Luts& luts) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 0905663..42ddcd1 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -42,7 +42,6 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
 #include <aidl/android/hardware/graphics/common/Transform.h>
@@ -307,7 +306,7 @@
                                         int32_t frameIntervalNs) = 0;
     virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers,
                                    std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
-    virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0;
+    virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 1df2ab1..5355a12 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -42,7 +42,8 @@
 using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using aidl::android::hardware::graphics::composer3::DisplayLuts;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::LutProperties;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -624,10 +625,18 @@
         auto layer = getLayerById(layerIds[i]);
         if (layer) {
             auto& layerLut = tmpLuts[i];
-            outLuts->emplace_or_replace(layer.get(), layerLut.lut.lutProperties);
-            lutFileDescriptorMapper.emplace_or_replace(layer.get(),
-                                                       ndk::ScopedFileDescriptor(
-                                                               layerLut.lut.pfd.release()));
+            if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) {
+                const auto& offsets = layerLut.luts.offsets.value();
+                std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties;
+                lutOffsetsAndProperties.reserve(offsets.size());
+                std::transform(offsets.begin(), offsets.end(), layerLut.luts.lutProperties.begin(),
+                               std::back_inserter(lutOffsetsAndProperties),
+                               [](int32_t i, LutProperties j) { return std::make_pair(i, j); });
+                outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties);
+                lutFileDescriptorMapper.emplace_or_replace(layer.get(),
+                                                           ndk::ScopedFileDescriptor(
+                                                                   layerLut.luts.pfd.release()));
+            }
         }
     }
 
@@ -1069,7 +1078,7 @@
     return static_cast<Error>(intError);
 }
 
-Error Layer::setLuts(std::vector<Lut>& luts) {
+Error Layer::setLuts(aidl::android::hardware::graphics::composer3::Luts& luts) {
     if (CC_UNLIKELY(!mDisplay)) {
         return Error::BAD_DISPLAY;
     }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 61f92f4..799fd02 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -46,7 +46,7 @@
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 
@@ -109,9 +109,10 @@
     virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0;
 
     static const int kLutFileDescriptorMapperSize = 20;
+    using LutOffsetAndProperties = std::vector<
+            std::pair<int32_t, aidl::android::hardware::graphics::composer3::LutProperties>>;
     using LayerLuts =
-            ftl::SmallMap<HWC2::Layer*, aidl::android::hardware::graphics::composer3::LutProperties,
-                          kLutFileDescriptorMapperSize>;
+            ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>;
     using LutFileDescriptorMapper =
             ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
 
@@ -375,7 +376,7 @@
     [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0;
     [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
     [[nodiscard]] virtual hal::Error setLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0;
+            aidl::android::hardware::graphics::composer3::Luts& luts) = 0;
 };
 
 namespace impl {
@@ -426,8 +427,7 @@
     // AIDL HAL
     hal::Error setBrightness(float brightness) override;
     hal::Error setBlockingRegion(const android::Region& region) override;
-    hal::Error setLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
+    hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override;
 
 private:
     // These are references to data owned by HWComposer, which will outlive
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 056ecd7..6a7a09b 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -47,7 +47,7 @@
 using aidl::android::hardware::graphics::composer3::DimmingStage;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using aidl::android::hardware::graphics::composer3::DisplayLuts;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -1415,7 +1415,7 @@
     return Error::NONE;
 }
 
-Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) {
+Error HidlComposer::setLayerLuts(Display, Layer, Luts&) {
     return Error::NONE;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 1cc23d1..a3d1f7f 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -356,7 +356,7 @@
             std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
             override;
     Error setLayerLuts(Display, Layer,
-                       std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override;
+                       aidl::android::hardware::graphics::composer3::Luts&) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index e5f6b7b..11b674b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -513,6 +513,10 @@
         isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f;
         blendMode = getBlendMode(requested);
     }
+
+    if (forceUpdate || requested.what & layer_state_t::eLutsChanged) {
+        luts = requested.luts;
+    }
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md
index e5f51a5..6258f7e 100644
--- a/services/surfaceflinger/FrontEnd/readme.md
+++ b/services/surfaceflinger/FrontEnd/readme.md
@@ -17,6 +17,29 @@
 This allows control to be delegated to different parts of the system - such as SystemServer,
 SysUI and Apps.
 
+### Layer Drawing Order
+Layers are drawn based on an inorder traversal, treating relative parents as
+direct parents. Negative z-values place layers below their parent, while
+non-negative values place them above. Layers with the same z-value are drawn
+in creation order (newer on top).  However, relying on creation order for
+z-ordering is discouraged; use unique z-values whenever possible for better
+control.
+
+Traversal pseudo code:
+```
+fn traverseBottomToTop(root):
+  for each child node including relative children,
+    sorted by z then layer id, with z less than 0:
+          traverseBottomToTop(childNode)
+
+  visit(root)
+
+  for each child node including relative children,
+    sorted by z then layer id, with z greater than
+    or equal to 0:
+          traverseBottomToTop(childNode)
+```
+
 ### Layer Lifecycle
 Layer is created by a client. The client receives a strong binder reference to the layer
 handle, which will keep the layer alive as long as the client holds the reference. The
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c88092b..20ba45f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -689,8 +689,20 @@
         listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
     }
 
-    if (mBufferReleaseChannel) {
-        mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount);
+    if (!mBufferReleaseChannel) {
+        return;
+    }
+
+    status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence,
+                                                               currentMaxAcquiredBufferCount);
+    if (status != OK) {
+        int error = -status;
+        // callReleaseBufferCallback is called during Layer's destructor. In this case, it's
+        // expected to receive connection errors.
+        if (error != EPIPE && error != ECONNRESET) {
+            ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error,
+                  strerror(error));
+        }
     }
 }
 
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index f64ba9e..a346981 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -174,6 +174,7 @@
     layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
     // Record the name of the layer for debugging further down the stack.
     layerSettings.name = mSnapshot->name;
+    layerSettings.luts = mSnapshot->luts;
 
     if (hasEffect() && !hasBufferOrSidebandStream()) {
         prepareEffectsClientComposition(layerSettings, targetSettings);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 09dbc59..d35a76a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1656,19 +1656,22 @@
         outProperties->combinations.emplace_back(outCombination);
     }
     outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
-    if (aidlProperties.lutProperties.has_value()) {
+    if (aidlProperties.lutProperties) {
         std::vector<gui::LutProperties> outLutProperties;
-        for (const auto& properties : aidlProperties.lutProperties.value()) {
-            gui::LutProperties currentProperties;
-            currentProperties.dimension =
-                    static_cast<gui::LutProperties::Dimension>(properties->dimension);
-            currentProperties.size = properties->size;
-            currentProperties.samplingKeys.reserve(properties->samplingKeys.size());
-            std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(),
-                           std::back_inserter(currentProperties.samplingKeys), [](const auto& val) {
-                               return static_cast<gui::LutProperties::SamplingKey>(val);
-                           });
-            outLutProperties.push_back(std::move(currentProperties));
+        for (auto properties : *aidlProperties.lutProperties) {
+            if (!properties) {
+                gui::LutProperties currentProperties;
+                currentProperties.dimension =
+                        static_cast<gui::LutProperties::Dimension>(properties->dimension);
+                currentProperties.size = properties->size;
+                currentProperties.samplingKeys.reserve(properties->samplingKeys.size());
+                std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(),
+                               std::back_inserter(currentProperties.samplingKeys),
+                               [](const auto& val) {
+                                   return static_cast<gui::LutProperties::SamplingKey>(val);
+                               });
+                outLutProperties.push_back(std::move(currentProperties));
+            }
         }
         outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end());
     }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index de4825b..b22ec66 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -144,7 +144,7 @@
                                                     eventStats, handle->previousReleaseCallbackId);
         if (handle->bufferReleaseChannel &&
             handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
-            mBufferReleases.emplace_back(handle->bufferReleaseChannel,
+            mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
                                          handle->previousReleaseCallbackId,
                                          handle->previousReleaseFence,
                                          handle->currentMaxAcquiredBufferCount);
@@ -159,8 +159,13 @@
 
 void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
     for (const auto& bufferRelease : mBufferReleases) {
-        bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
-                                                 bufferRelease.currentMaxAcquiredBufferCount);
+        status_t status = bufferRelease.channel
+                                  ->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
+                                                      bufferRelease.currentMaxAcquiredBufferCount);
+        if (status != OK) {
+            ALOGE("[%s] writeReleaseFence failed. error %d (%s)", bufferRelease.layerName.c_str(),
+                  -status, strerror(-status));
+        }
     }
     mBufferReleases.clear();
 
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index d81d8d0..178ddbb 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -83,6 +83,7 @@
         mCompletedTransactions;
 
     struct BufferRelease {
+        std::string layerName;
         std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel;
         ReleaseCallbackId callbackId;
         sp<Fence> fence;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 8a81c56..658bca6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -156,6 +156,7 @@
     DUMP_READ_ONLY_FLAG(single_hop_screenshot);
     DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
     DUMP_READ_ONLY_FLAG(true_hdr_screenshots);
+    DUMP_READ_ONLY_FLAG(display_config_error_hal);
 
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
@@ -260,6 +261,7 @@
 FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
 FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
 FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
+FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, "");
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index b097bf9..4f34718 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -94,6 +94,7 @@
     bool single_hop_screenshot() const;
     bool trace_frame_rate_override() const;
     bool true_hdr_screenshots() const;
+    bool display_config_error_hal() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index ae502cf..1de6b4a 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -28,16 +28,18 @@
 cc_defaults {
     name: "surfaceflinger_fuzz_defaults",
     static_libs: [
-        "libc++fs",
         "libsurfaceflinger_common",
     ],
     srcs: [
+        ":libsurfaceflinger_backend_mock_sources",
+        ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
     ],
     defaults: [
         "libsurfaceflinger_defaults",
     ],
     header_libs: [
+        "libsurfaceflinger_backend_mock_headers",
         "libsurfaceflinger_headers",
     ],
     cflags: [
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d724dfc..f758879 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -82,6 +82,14 @@
 } # detached_mirror
 
 flag {
+  name: "display_config_error_hal"
+  namespace: "core_graphics"
+  description: "Report HAL display configuration errors like modeset failure or link training failure"
+  bug: "374184110"
+  is_fixed_read_only: true
+} # display_config_error_hal
+
+flag {
   name: "filter_frames_before_trace_starts"
   namespace: "core_graphics"
   description: "Do not trace FrameTimeline events for frames started before the trace started"
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index bce1406..6eb7f4a 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -35,9 +35,6 @@
         ":libsurfaceflinger_mock_sources",
         "TransactionTraceTestSuite.cpp",
     ],
-    static_libs: [
-        "libc++fs",
-    ],
     header_libs: [
         "libsurfaceflinger_mocks_headers",
     ],
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 40a6fb8..cb8820a 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -22,15 +22,40 @@
     default_team: "trendy_team_android_core_graphics_stack",
 }
 
+// This is a step towards pulling out the "backend" sources to clean up the
+// dependency graph between CompositionEngine and SurfaceFlinger.
+// MockNativeWindow doesn't strictly belong here, but this works for now so
+// that CompositionEngine tests can use these mocks.
 filegroup {
-    name: "libsurfaceflinger_mock_sources",
+    name: "libsurfaceflinger_backend_mock_sources",
     srcs: [
-        "mock/DisplayHardware/MockPowerHalController.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
+        "mock/DisplayHardware/MockHWComposer.cpp",
         "mock/DisplayHardware/MockIPower.cpp",
         "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
+        "mock/DisplayHardware/MockPowerHalController.cpp",
+        "mock/system/window/MockNativeWindow.cpp",
+    ],
+}
+
+cc_library_headers {
+    name: "libsurfaceflinger_backend_mock_headers",
+    export_include_dirs: ["."],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    export_static_lib_headers: [
+        "libgmock",
+        "libgtest",
+    ],
+}
+
+filegroup {
+    name: "libsurfaceflinger_mock_sources",
+    srcs: [
         "mock/MockEventThread.cpp",
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
@@ -39,7 +64,6 @@
         "mock/MockVsyncController.cpp",
         "mock/MockVSyncDispatch.cpp",
         "mock/MockVSyncTracker.cpp",
-        "mock/system/window/MockNativeWindow.cpp",
     ],
 }
 
@@ -57,9 +81,9 @@
         "surfaceflinger_defaults",
     ],
     test_suites: ["device-tests"],
-    static_libs: ["libc++fs"],
     header_libs: ["surfaceflinger_tests_common_headers"],
     srcs: [
+        ":libsurfaceflinger_backend_mock_sources",
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 615cc94..3e6a768 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -185,7 +185,7 @@
             (Display, std::vector<Layer>*,
              std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
     MOCK_METHOD(Error, setLayerLuts,
-                (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
+                (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 53ed2e1..121104d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -126,6 +126,8 @@
                 (uint32_t, const android::sp<android::GraphicBuffer> &,
                  const android::sp<android::Fence> &),
                 (override));
+    MOCK_METHOD(hal::Error, setBufferSlotsToClear,
+                (const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot), (override));
     MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
     MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
     MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color),
@@ -147,8 +149,8 @@
                 (const std::string &, bool, const std::vector<uint8_t> &), (override));
     MOCK_METHOD(hal::Error, setBrightness, (float), (override));
     MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
-    MOCK_METHOD(hal::Error, setLuts,
-                (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override));
+    MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&),
+                (override));
 };
 
 } // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
similarity index 81%
rename from services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
index ae52670..f310633 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
@@ -16,17 +16,11 @@
 
 #include "MockHWComposer.h"
 
-namespace android {
-
-// This will go away once HWComposer is moved into the "backend" library
-HWComposer::~HWComposer() = default;
-
-namespace mock {
+namespace android::mock {
 
 // The Google Mock documentation recommends explicit non-header instantiations
 // for better compile time performance.
 HWComposer::HWComposer() = default;
 HWComposer::~HWComposer() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
new file mode 100644
index 0000000..fa7128c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::mock {
+
+class HWComposer : public android::HWComposer {
+public:
+    using HWDisplayId = android::hardware::graphics::composer::hal::HWDisplayId;
+    using PowerMode = android::hardware::graphics::composer::hal::PowerMode;
+
+    HWComposer();
+    ~HWComposer() override;
+
+    MOCK_METHOD(void, setCallback, (HWC2::ComposerCallback&), (override));
+    MOCK_METHOD(bool, getDisplayIdentificationData,
+                (HWDisplayId, uint8_t*, DisplayIdentificationData*), (const, override));
+    MOCK_METHOD(bool, hasCapability, (aidl::android::hardware::graphics::composer3::Capability),
+                (const, override));
+    MOCK_METHOD(bool, hasDisplayCapability,
+                (HalDisplayId, aidl::android::hardware::graphics::composer3::DisplayCapability),
+                (const, override));
+
+    MOCK_METHOD(size_t, getMaxVirtualDisplayCount, (), (const, override));
+    MOCK_METHOD(size_t, getMaxVirtualDisplayDimension, (), (const, override));
+    MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*),
+                (override));
+    MOCK_METHOD(void, allocatePhysicalDisplay,
+                (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override));
+
+    MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override));
+    MOCK_METHOD(status_t, getDeviceCompositionChanges,
+                (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+                 Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+    MOCK_METHOD(status_t, setClientTarget,
+                (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
+                 float),
+                (override));
+    MOCK_METHOD(status_t, presentAndGetReleaseFences,
+                (HalDisplayId, std::optional<std::chrono::steady_clock::time_point>), (override));
+    MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
+    MOCK_METHOD(status_t, setPowerMode, (PhysicalDisplayId, PowerMode), (override));
+    MOCK_METHOD(status_t, setColorTransform, (HalDisplayId, const mat4&), (override));
+    MOCK_METHOD(void, disconnectDisplay, (HalDisplayId), (override));
+    MOCK_METHOD(sp<Fence>, getPresentFence, (HalDisplayId), (const, override));
+    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(sp<Fence>, getLayerReleaseFence, (HalDisplayId, HWC2::Layer*), (const, override));
+    MOCK_METHOD(status_t, setOutputBuffer,
+                (HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&), (override));
+    MOCK_METHOD(void, clearReleaseFences, (HalDisplayId), (override));
+    MOCK_METHOD(status_t, getHdrCapabilities, (HalDisplayId, HdrCapabilities*), (override));
+    MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (HalDisplayId), (const, override));
+    MOCK_METHOD(std::vector<ui::RenderIntent>, getRenderIntents, (HalDisplayId, ui::ColorMode),
+                (const, override));
+    MOCK_METHOD(mat4, getDataspaceSaturationMatrix, (HalDisplayId, ui::Dataspace), (override));
+    MOCK_METHOD(status_t, getDisplayedContentSamplingAttributes,
+                (HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*), (override));
+    MOCK_METHOD(status_t, setDisplayContentSamplingEnabled, (HalDisplayId, bool, uint8_t, uint64_t),
+                (override));
+    MOCK_METHOD(status_t, getDisplayedContentSample,
+                (HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*), (override));
+    MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
+                (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
+                (override));
+    MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug,
+                (hal::HWDisplayId, hal::Connection), (override));
+    MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override));
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
+    MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override));
+    MOCK_METHOD(bool, isConnected, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(std::vector<HWComposer::HWCDisplayMode>, getModes, (PhysicalDisplayId, int32_t),
+                (const, override));
+    MOCK_METHOD((ftl::Expected<hal::HWConfigId, status_t>), getActiveMode, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(std::vector<ui::ColorMode>, getColorModes, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(status_t, setActiveColorMode, (PhysicalDisplayId, ui::ColorMode, ui::RenderIntent),
+                (override));
+    MOCK_METHOD(ui::DisplayConnectionType, getDisplayConnectionType, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD((ftl::Expected<nsecs_t, status_t>), getDisplayVsyncPeriod, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(status_t, setActiveModeWithConstraints,
+                (PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+                 hal::VsyncPeriodChangeTimeline*),
+                (override));
+    MOCK_METHOD(status_t, setBootDisplayMode, (PhysicalDisplayId, hal::HWConfigId), (override));
+    MOCK_METHOD(status_t, clearBootDisplayMode, (PhysicalDisplayId), (override));
+    MOCK_METHOD(std::optional<hal::HWConfigId>, getPreferredBootDisplayMode, (PhysicalDisplayId),
+                (override));
+
+    MOCK_METHOD(std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>,
+                getHdrConversionCapabilities, (), (const, override));
+    MOCK_METHOD(status_t, setHdrConversionStrategy,
+                (aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                 aidl::android::hardware::graphics::common::Hdr*),
+                (override));
+    MOCK_METHOD(status_t, setAutoLowLatencyMode, (PhysicalDisplayId, bool), (override));
+    MOCK_METHOD(status_t, getSupportedContentTypes,
+                (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
+    MOCK_METHOD(status_t, setContentType, (PhysicalDisplayId, hal::ContentType)), (override);
+    MOCK_METHOD((const std::unordered_map<std::string, bool>&), getSupportedLayerGenericMetadata,
+                (), (const, override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
+    MOCK_METHOD(void, dumpOverlayProperties, (std::string&), (const, override));
+    MOCK_METHOD(android::Hwc2::Composer*, getComposer, (), (const, override));
+
+    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+    MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+                (const, override));
+    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(status_t, getDisplayDecorationSupport,
+                (PhysicalDisplayId,
+                 std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                         support),
+                (override));
+    MOCK_METHOD(status_t, setIdleTimerEnabled, (PhysicalDisplayId, std::chrono::milliseconds),
+                (override));
+    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
+    MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
+                getOverlaySupport, (), (const, override));
+    MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
+    MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
+    MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (),
+                (override));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index ed1405b..4c034d7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -32,6 +32,7 @@
 using aidl::android::hardware::power::IPowerHintSession;
 using aidl::android::hardware::power::SessionConfig;
 using aidl::android::hardware::power::SessionTag;
+using aidl::android::hardware::power::SupportInfo;
 
 using aidl::android::hardware::power::Mode;
 using android::binder::Status;
@@ -59,6 +60,7 @@
     MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
                 (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
     MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
     MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));