Merge "SF: Add a readonly feature flag for display config error HAL changes" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9c01169..07d16f7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,9 +4,6 @@
       "name": "SurfaceFlinger_test",
       "options": [
         {
-          "include-filter": "*"
-        },
-        {
           // TODO(b/305717998): Deflake and re-enable
           "exclude-filter": "*ChildLayerTest*"
         }
@@ -23,12 +20,7 @@
   ],
   "hwasan-postsubmit": [
     {
-      "name": "SurfaceFlinger_test",
-      "options": [
-        {
-          "include-filter": "*"
-        }
-      ]
+      "name": "SurfaceFlinger_test"
     }
   ]
 }
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/Resampler.h b/include/input/Resampler.h
index 47519c2..6d95ca7 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -16,10 +16,14 @@
 
 #pragma once
 
+#include <array>
 #include <chrono>
+#include <iterator>
 #include <optional>
 #include <vector>
 
+#include <android-base/logging.h>
+#include <ftl/mixins.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 #include <input/RingBuffer.h>
@@ -79,13 +83,127 @@
         PointerCoords coords;
     };
 
+    /**
+     * Container that stores pointers as an associative array, supporting O(1) lookup by pointer id,
+     * as well as forward iteration in the order in which the pointer or pointers were inserted in
+     * the container. PointerMap has a maximum capacity equal to MAX_POINTERS.
+     */
+    class PointerMap {
+    public:
+        struct PointerId : ftl::DefaultConstructible<PointerId, int32_t>,
+                           ftl::Equatable<PointerId> {
+            using DefaultConstructible::DefaultConstructible;
+        };
+
+        /**
+         * Custom iterator to enable use of range-based for loops.
+         */
+        template <typename T>
+        class iterator {
+        public:
+            using iterator_category = std::forward_iterator_tag;
+            using value_type = T;
+            using difference_type = std::ptrdiff_t;
+            using pointer = T*;
+            using reference = T&;
+
+            explicit iterator(pointer element) : mElement{element} {}
+
+            friend bool operator==(const iterator& lhs, const iterator& rhs) {
+                return lhs.mElement == rhs.mElement;
+            }
+
+            friend bool operator!=(const iterator& lhs, const iterator& rhs) {
+                return !(lhs == rhs);
+            }
+
+            iterator operator++() {
+                ++mElement;
+                return *this;
+            }
+
+            reference operator*() const { return *mElement; }
+
+        private:
+            pointer mElement;
+        };
+
+        PointerMap() {
+            idToIndex.fill(std::nullopt);
+            for (Pointer& pointer : pointers) {
+                pointer.properties.clear();
+                pointer.coords.clear();
+            }
+        }
+
+        /**
+         * Forward iterators to traverse the pointers in `pointers`. The order of the pointers is
+         * determined by the order in which they were inserted (not by id).
+         */
+        iterator<Pointer> begin() { return iterator<Pointer>{&pointers[0]}; }
+
+        iterator<const Pointer> begin() const { return iterator<const Pointer>{&pointers[0]}; }
+
+        iterator<Pointer> end() { return iterator<Pointer>{&pointers[nextPointerIndex]}; }
+
+        iterator<const Pointer> end() const {
+            return iterator<const Pointer>{&pointers[nextPointerIndex]};
+        }
+
+        /**
+         * Inserts the given pointer into the PointerMap. Precondition: The current number of
+         * contained pointers must be less than MAX_POINTERS when this function is called. It
+         * fatally logs if the user tries to insert more than MAX_POINTERS, or if pointer id is out
+         * of bounds.
+         */
+        void insert(const Pointer& pointer) {
+            LOG_IF(FATAL, nextPointerIndex >= pointers.size())
+                    << "Cannot insert more than " << MAX_POINTERS << " in PointerMap.";
+            LOG_IF(FATAL, (pointer.properties.id < 0) || (pointer.properties.id > MAX_POINTER_ID))
+                    << "Invalid pointer id.";
+            idToIndex[pointer.properties.id] = std::optional<size_t>{nextPointerIndex};
+            pointers[nextPointerIndex] = pointer;
+            ++nextPointerIndex;
+        }
+
+        /**
+         * Returns the pointer associated with the provided id if it exists.
+         * Otherwise, std::nullopt is returned.
+         */
+        std::optional<Pointer> find(PointerId id) const {
+            const int32_t idValue = ftl::to_underlying(id);
+            LOG_IF(FATAL, (idValue < 0) || (idValue > MAX_POINTER_ID)) << "Invalid pointer id.";
+            const std::optional<size_t> index = idToIndex[idValue];
+            return index.has_value() ? std::optional{pointers[*index]} : std::nullopt;
+        }
+
+    private:
+        /**
+         * The index at which a pointer is inserted in `pointers`. Likewise, it represents the
+         * number of pointers in PointerMap.
+         */
+        size_t nextPointerIndex{0};
+
+        /**
+         * Sequentially stores pointers. Each pointer's position is determined by the value of
+         * nextPointerIndex at insertion time.
+         */
+        std::array<Pointer, MAX_POINTERS + 1> pointers;
+
+        /**
+         * Maps each pointer id to its associated index in pointers. If no pointer with the id
+         * exists in pointers, the mapped value is std::nullopt.
+         */
+        std::array<std::optional<size_t>, MAX_POINTER_ID + 1> idToIndex;
+    };
+
     struct Sample {
         std::chrono::nanoseconds eventTime;
-        std::vector<Pointer> pointers;
+        PointerMap pointerMap;
 
         std::vector<PointerCoords> asPointerCoords() const {
             std::vector<PointerCoords> pointersCoords;
-            for (const Pointer& pointer : pointers) {
+            for (const Pointer& pointer : pointerMap) {
                 pointersCoords.push_back(pointer.coords);
             }
             return pointersCoords;
@@ -100,13 +218,12 @@
     RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
 
     /**
-     * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
-     * does not move between samples.
+     * Latest sample in mLatestSamples after resampling motion event.
      */
     std::optional<Sample> mLastRealSample;
 
     /**
-     * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
+     * Latest prediction. That is, the latest extrapolated sample.
      */
     std::optional<Sample> mPreviousPrediction;
 
@@ -134,12 +251,12 @@
     bool canInterpolate(const InputMessage& futureSample) const;
 
     /**
-     * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+     * Returns a sample interpolated between the latest sample of mLatestSamples and futureMessage,
      * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
      * mLatestSamples must have at least one sample when attemptInterpolation is called.
      */
     std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
-                                               const InputMessage& futureSample) const;
+                                               const InputMessage& futureMessage) const;
 
     /**
      * Checks if there are necessary conditions to extrapolate. That is, there are at least two
@@ -156,7 +273,8 @@
     std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
 
     /**
-     * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
+     * Iterates through motion event samples, and replaces real coordinates with resampled
+     * coordinates to avoid jerkiness in certain conditions.
      */
     void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
 
@@ -174,4 +292,5 @@
 
     inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
 };
+
 } // namespace android
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/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/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/Resampler.cpp b/libs/input/Resampler.cpp
index e2cc6fb..056db09 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <iomanip>
 #include <ostream>
 
 #include <android-base/logging.h>
@@ -37,6 +38,11 @@
         true;
 #endif
 
+/**
+ * Log debug messages about timestamp and coordinates of event resampling.
+ * Enable this via "adb shell setprop log.tag.LegacyResamplerResampling DEBUG"
+ * (requires restart)
+ */
 bool debugResampling() {
     if (!IS_DEBUGGABLE_BUILD) {
         static const bool DEBUG_TRANSPORT_RESAMPLING =
@@ -107,46 +113,44 @@
     const size_t latestIndex = numSamples - 1;
     const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
     for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
-        std::vector<Pointer> pointers;
-        const size_t numPointers = motionEvent.getPointerCount();
-        for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
-            pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
-                                       *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
-                                                                                   sampleIndex))});
+        PointerMap pointerMap;
+        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+             ++pointerIndex) {
+            pointerMap.insert(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
+                                      *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
+                                                                                  sampleIndex))});
         }
         mLatestSamples.pushBack(
-                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
+                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointerMap});
     }
 }
 
 LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
-    std::vector<Pointer> pointers;
+    PointerMap pointerMap;
     for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
-        pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
-                                   message.body.motion.pointers[i].coords});
+        pointerMap.insert(Pointer{message.body.motion.pointers[i].properties,
+                                  message.body.motion.pointers[i].coords});
     }
-    return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
+    return Sample{nanoseconds{message.body.motion.eventTime}, pointerMap};
 }
 
 bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
-    if (target.pointers.size() > auxiliary.pointers.size()) {
-        LOG_IF(INFO, debugResampling())
-                << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
-        return false;
-    }
-    for (size_t i = 0; i < target.pointers.size(); ++i) {
-        if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
-            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+    for (const Pointer& pointer : target.pointerMap) {
+        const std::optional<Pointer> auxiliaryPointer =
+                auxiliary.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+        if (!auxiliaryPointer.has_value()) {
+            LOG_IF(INFO, debugResampling())
+                    << "Not resampled. Auxiliary sample does not contain all pointers from target.";
             return false;
         }
-        if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+        if (pointer.properties.toolType != auxiliaryPointer->properties.toolType) {
             LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
             return false;
         }
-        if (!canResampleTool(target.pointers[i].properties.toolType)) {
+        if (!canResampleTool(pointer.properties.toolType)) {
             LOG_IF(INFO, debugResampling())
                     << "Not resampled. Cannot resample "
-                    << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+                    << ftl::enum_string(pointer.properties.toolType) << " ToolType.";
             return false;
         }
     }
@@ -166,35 +170,40 @@
 
     const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too small: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     }
     return true;
 }
 
 std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
-        nanoseconds resampleTime, const InputMessage& futureSample) const {
-    if (!canInterpolate(futureSample)) {
+        nanoseconds resampleTime, const InputMessage& futureMessage) const {
+    if (!canInterpolate(futureMessage)) {
         return std::nullopt;
     }
     LOG_IF(FATAL, mLatestSamples.empty())
             << "Not resampled. mLatestSamples must not be empty to interpolate.";
 
     const Sample& pastSample = *(mLatestSamples.end() - 1);
+    const Sample& futureSample = messageToSample(futureMessage);
 
-    const nanoseconds delta =
-            nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
+    const nanoseconds delta = nanoseconds{futureSample.eventTime} - pastSample.eventTime;
     const float alpha =
-            std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+            std::chrono::duration<float, std::nano>(resampleTime - pastSample.eventTime) / delta;
 
-    std::vector<Pointer> resampledPointers;
-    for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
-        const PointerCoords& resampledCoords =
-                calculateResampledCoords(pastSample.pointers[i].coords,
-                                         futureSample.body.motion.pointers[i].coords, alpha);
-        resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+    PointerMap resampledPointerMap;
+    for (const Pointer& pointer : pastSample.pointerMap) {
+        if (std::optional<Pointer> futureSamplePointer =
+                    futureSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+            futureSamplePointer.has_value()) {
+            const PointerCoords& resampledCoords =
+                    calculateResampledCoords(pointer.coords, futureSamplePointer->coords, alpha);
+            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
+        }
     }
-    return Sample{resampleTime, resampledPointers};
+    return Sample{resampleTime, resampledPointerMap};
 }
 
 bool LegacyResampler::canExtrapolate() const {
@@ -212,10 +221,14 @@
 
     const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too small: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     } else if (delta > RESAMPLE_MAX_DELTA) {
-        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Delta is too large: " << std::setprecision(3)
+                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
         return false;
     }
     return true;
@@ -241,20 +254,28 @@
             (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
     LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
             << "Resample time is too far in the future. Adjusting prediction from "
-            << (resampleTime - presentSample.eventTime) << " to "
-            << (farthestPrediction - presentSample.eventTime) << "ns.";
+            << std::setprecision(3)
+            << std::chrono::duration<double, std::milli>{resampleTime - presentSample.eventTime}
+                       .count()
+            << "ms to "
+            << std::chrono::duration<double, std::milli>{farthestPrediction -
+                                                         presentSample.eventTime}
+                       .count()
+            << "ms";
     const float alpha =
-            std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
-            delta;
+            std::chrono::duration<float, std::nano>(newResampleTime - pastSample.eventTime) / delta;
 
-    std::vector<Pointer> resampledPointers;
-    for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
-        const PointerCoords& resampledCoords =
-                calculateResampledCoords(pastSample.pointers[i].coords,
-                                         presentSample.pointers[i].coords, alpha);
-        resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+    PointerMap resampledPointerMap;
+    for (const Pointer& pointer : presentSample.pointerMap) {
+        if (std::optional<Pointer> pastSamplePointer =
+                    pastSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
+            pastSamplePointer.has_value()) {
+            const PointerCoords& resampledCoords =
+                    calculateResampledCoords(pastSamplePointer->coords, pointer.coords, alpha);
+            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
+        }
     }
-    return Sample{newResampleTime, resampledPointers};
+    return Sample{newResampleTime, resampledPointerMap};
 }
 
 inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
@@ -267,6 +288,12 @@
     return RESAMPLE_LATENCY;
 }
 
+/**
+ * The resampler is unaware of ACTION_DOWN. Thus, it needs to constantly check for pointer IDs
+ * occurrences. This problem could be fixed if the resampler has access to the entire stream of
+ * MotionEvent actions. That way, both ACTION_DOWN and ACTION_UP will be visible; therefore,
+ * facilitating pointer tracking between samples.
+ */
 void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
     const size_t numSamples = motionEvent.getHistorySize() + 1;
     for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
@@ -276,34 +303,59 @@
 }
 
 void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+    if (!mLastRealSample.has_value() || !mPreviousPrediction.has_value()) {
+        LOG_IF(INFO, debugResampling()) << "Still pointers not overwritten. Not enough data.";
+        return;
+    }
     for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
+        const std::optional<Pointer> lastRealPointer = mLastRealSample->pointerMap.find(
+                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+        const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
+                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+        // This could happen because resampler only receives ACTION_MOVE events.
+        if (!lastRealPointer.has_value() || !previousPointer.has_value()) {
+            continue;
+        }
         const PointerCoords& pointerCoords =
                 *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
-        if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
+        if (equalXY(pointerCoords, lastRealPointer->coords)) {
             LOG_IF(INFO, debugResampling())
                     << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
                     << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
-                    << mLastRealSample->pointers[pointerIndex].coords;
+                    << previousPointer->coords;
             setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
-                                        mPreviousPrediction->pointers[pointerIndex].coords);
+                                        previousPointer->coords);
         }
     }
 }
 
 void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
     if (!mPreviousPrediction.has_value()) {
+        LOG_IF(INFO, debugResampling()) << "Old sample not overwritten. Not enough data.";
         return;
     }
     if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
         mPreviousPrediction->eventTime) {
         LOG_IF(INFO, debugResampling())
                 << "Motion event sample older than predicted sample. Overwriting event time from "
-                << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
-                << mPreviousPrediction->eventTime.count() << "ns.";
+                << std::setprecision(3)
+                << std::chrono::duration<double,
+                                         std::milli>{nanoseconds{motionEvent.getHistoricalEventTime(
+                                                             sampleIndex)}}
+                           .count()
+                << "ms to "
+                << std::chrono::duration<double, std::milli>{mPreviousPrediction->eventTime}.count()
+                << "ms";
         for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
              ++pointerIndex) {
+            const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
+                    PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
+            // This could happen because resampler only receives ACTION_MOVE events.
+            if (!previousPointer.has_value()) {
+                continue;
+            }
             setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
-                                        mPreviousPrediction->pointers[pointerIndex].coords);
+                                        previousPointer->coords);
         }
     }
 }
@@ -333,6 +385,7 @@
             mPreviousPrediction = sample;
         }
     }
+    LOG_IF(FATAL, mLatestSamples.empty()) << "mLatestSamples must contain at least one sample.";
     mLastRealSample = *(mLatestSamples.end() - 1);
 }
 
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 701fb43..7acfafb 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."
@@ -214,3 +207,10 @@
   description: "Enable telemetry for rotary input"
   bug: "370353565"
 }
+
+flag {
+  name: "set_input_device_kernel_wake"
+  namespace: "input"
+  description: "Set input device's power/wakeup sysfs node"
+  bug: "372812925"
+}
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index fae8518..3162b77 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -648,7 +648,15 @@
 
     mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
 
-    assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+    assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+                                              {Pointer{.id = 0,
+                                                       .x = 1.4f,
+                                                       .y = 1.4f,
+                                                       .isResampled = true},
+                                               Pointer{.id = 1,
+                                                       .x = 2.4f,
+                                                       .y = 2.4f,
+                                                       .isResampled = true}});
 }
 
 TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {
@@ -670,7 +678,15 @@
 
     mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
 
-    assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+    assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
+                                              {Pointer{.id = 1,
+                                                       .x = 4.4f,
+                                                       .y = 4.4f,
+                                                       .isResampled = true},
+                                               Pointer{.id = 0,
+                                                       .x = 3.4f,
+                                                       .y = 3.4f,
+                                                       .isResampled = true}});
 }
 
 TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) {
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d05ff34..5a78a5c 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -175,6 +175,14 @@
     AHARDWAREBUFFER_FORMAT_YCbCr_P010               = 0x36,
 
     /**
+     * YUV P210 format.
+     * Must have an even width and height. Can be accessed in OpenGL
+     * shaders through an external sampler. Does not support mip-maps
+     * cube-maps or multi-layered textures.
+     */
+    AHARDWAREBUFFER_FORMAT_YCbCr_P210               = 0x3c,
+
+    /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R8_UNORM
      *   OpenGL ES: GR_GL_R8
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index c572ee7..faab48b 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -110,6 +110,7 @@
     name: "libnativewindow_defaults",
     srcs: ["src/lib.rs"],
     rustlibs: [
+        "android.hardware.common-V2-rust",
         "libbinder_rs",
         "libbitflags",
         "libnativewindow_bindgen",
diff --git a/libs/nativewindow/rust/src/handle.rs b/libs/nativewindow/rust/src/handle.rs
index c41ab8d..2b08c1b 100644
--- a/libs/nativewindow/rust/src/handle.rs
+++ b/libs/nativewindow/rust/src/handle.rs
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use android_hardware_common::{
+    aidl::android::hardware::common::NativeHandle::NativeHandle as AidlNativeHandle,
+    binder::ParcelFileDescriptor,
+};
 use std::{
     ffi::c_int,
     mem::forget,
@@ -81,6 +85,12 @@
 
     /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained.
     pub fn into_fds(self) -> Vec<OwnedFd> {
+        // Unset FDSan tag since this `native_handle_t` is no longer the owner of the file
+        // descriptors after this function.
+        // SAFETY: Our wrapped `native_handle_t` pointer is always valid.
+        unsafe {
+            ffi::native_handle_unset_fdsan_tag(self.as_ref());
+        }
         let fds = self.data()[..self.fd_count()]
             .iter()
             .map(|fd| {
@@ -190,6 +200,21 @@
     }
 }
 
+impl From<AidlNativeHandle> for NativeHandle {
+    fn from(aidl_native_handle: AidlNativeHandle) -> Self {
+        let fds = aidl_native_handle.fds.into_iter().map(OwnedFd::from).collect();
+        Self::new(fds, &aidl_native_handle.ints).unwrap()
+    }
+}
+
+impl From<NativeHandle> for AidlNativeHandle {
+    fn from(native_handle: NativeHandle) -> Self {
+        let ints = native_handle.ints().to_owned();
+        let fds = native_handle.into_fds().into_iter().map(ParcelFileDescriptor::new).collect();
+        Self { ints, fds }
+    }
+}
+
 // SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file
 // descriptors, which aren't tied to any particular thread.
 unsafe impl Send for NativeHandle {}
@@ -240,4 +265,43 @@
 
         drop(cloned);
     }
+
+    #[test]
+    fn to_fds() {
+        let file = File::open("/dev/null").unwrap();
+        let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+        assert_eq!(original.ints(), &[42]);
+        assert_eq!(original.fds().len(), 1);
+
+        let fds = original.into_fds();
+        assert_eq!(fds.len(), 1);
+    }
+
+    #[test]
+    fn to_aidl() {
+        let file = File::open("/dev/null").unwrap();
+        let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+        assert_eq!(original.ints(), &[42]);
+        assert_eq!(original.fds().len(), 1);
+
+        let aidl = AidlNativeHandle::from(original);
+        assert_eq!(&aidl.ints, &[42]);
+        assert_eq!(aidl.fds.len(), 1);
+    }
+
+    #[test]
+    fn to_from_aidl() {
+        let file = File::open("/dev/null").unwrap();
+        let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+        assert_eq!(original.ints(), &[42]);
+        assert_eq!(original.fds().len(), 1);
+
+        let aidl = AidlNativeHandle::from(original);
+        assert_eq!(&aidl.ints, &[42]);
+        assert_eq!(aidl.fds.len(), 1);
+
+        let converted_back = NativeHandle::from(aidl);
+        assert_eq!(converted_back.ints(), &[42]);
+        assert_eq!(converted_back.fds().len(), 1);
+    }
 }
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index f19b908..a1d986e 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -220,7 +220,13 @@
         Self(buffer)
     }
 
-    /// Get the internal |AHardwareBuffer| pointer without decrementing the refcount. This can
+    /// 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.
+    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.
     pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
         let buffer = ManuallyDrop::new(self);
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 756a29b..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.
@@ -426,6 +425,16 @@
 
     /* Notifies that mouse cursor faded due to typing. */
     virtual void notifyMouseCursorFadedOnTyping() = 0;
+
+    /* Set whether the given input device can wake up the kernel from sleep
+     * when it generates input events. By default, usually only internal (built-in)
+     * input devices can wake the kernel from sleep. For an external input device
+     * that supports remote wakeup to be able to wake the kernel, this must be called
+     * after each time the device is connected/added.
+     *
+     * Returns true if setting power wakeup was successful.
+     */
+    virtual bool setKernelWakeEnabled(int32_t deviceId, bool enabled) = 0;
 };
 
 // --- TouchAffineTransformation ---
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f8cd973..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
@@ -2848,6 +2851,35 @@
     mNeedToReopenDevices = true;
 }
 
+bool EventHub::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    std::scoped_lock _l(mLock);
+    std::string enabledStr = enabled ? "enabled" : "disabled";
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr) {
+        ALOGE("Device Id %d does not exist for setting power wakeup", deviceId);
+        return false;
+    }
+    if (device->associatedDevice == nullptr) {
+        return false;
+    }
+    std::filesystem::path currentPath = device->associatedDevice->sysfsRootPath;
+    while (!currentPath.empty() && currentPath != "/") {
+        std::string nodePath = currentPath / "power/wakeup";
+        if (std::filesystem::exists(nodePath)) {
+            if (base::WriteStringToFile(enabledStr, nodePath)) {
+                return true;
+
+            }
+            // No need to continue searching in parent directories as power/wakeup nodes
+            // higher up may control other subdevices.
+            ALOGW("Failed to set power/wakeup node at %s", nodePath.c_str());
+            return false;
+        }
+        currentPath = currentPath.parent_path();
+    }
+    return false;
+}
+
 void EventHub::dump(std::string& dump) const {
     dump += "Event Hub State:\n";
 
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 02eeb0a..5e42d57 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -745,6 +745,14 @@
     }
 }
 
+bool InputDevice::setKernelWakeEnabled(bool enabled) {
+    bool success = false;
+    for_each_subdevice([&enabled, &success](InputDeviceContext& context) {
+        success |= context.setKernelWakeEnabled(enabled);
+    });
+    return success;
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ab27042..a2b7e82 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -901,6 +901,16 @@
     mPreventingTouchpadTaps = true;
 }
 
+bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->setKernelWakeEnabled(enabled);
+    }
+    return false;
+}
+
 void InputReader::dump(std::string& dump) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index dffd8e3..4336945 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -396,6 +396,13 @@
     /* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery,
      * etc. is detected. */
     virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
+
+    /* Set whether the given input device can wake up the kernel from sleep
+     * when it generates input events. By default, usually only internal (built-in)
+     * input devices can wake the kernel from sleep. For an external input device
+     * that supports remote wakeup to be able to wake the kernel, this must be called
+     * after each time the device is connected/added. */
+    virtual bool setKernelWakeEnabled(int32_t deviceId, bool enabled) = 0;
 };
 
 template <std::size_t BITS>
@@ -603,6 +610,8 @@
 
     void sysfsNodeChanged(const std::string& sysfsNodePath) override final;
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override final;
+
     ~EventHub() override;
 
 private:
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 8958d9e..abe7a5f 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -139,6 +139,8 @@
 
     std::optional<HardwareProperties> getTouchpadHardwareProperties();
 
+    bool setKernelWakeEnabled(bool enabled);
+
     // construct and add a mapper to the input device
     template <class T, typename... Args>
     T& addMapper(int32_t eventHubId, Args... args) {
@@ -469,6 +471,9 @@
     inline void setKeyboardType(KeyboardType keyboardType) {
         return mDevice.setKeyboardType(keyboardType);
     }
+    inline bool setKernelWakeEnabled(bool enabled) {
+        return mEventHub->setKernelWakeEnabled(mId, enabled);
+    }
 
 private:
     InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1003871..7614a05 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -120,6 +120,8 @@
 
     void notifyMouseCursorFadedOnTyping() override;
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
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/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/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 943de6e..e72c440 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -650,4 +650,25 @@
     }
 }
 
+bool FakeEventHub::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        return false;
+    }
+    mKernelWakeup.emplace(deviceId, enabled);
+    return true;
+}
+
+bool FakeEventHub::fakeReadKernelWakeup(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        return false;
+    }
+    auto it = mKernelWakeup.find(deviceId);
+    if (it == mKernelWakeup.end()) {
+        return false;
+    }
+    return it->second;
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 2dfbb23..143b93b 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -94,6 +94,8 @@
     // Simulates a device light intensities, from light id to light intensities map.
     std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
             mLightIntensities;
+    // fake sysfs node path and value.
+    std::unordered_map<int32_t /*deviceId*/, bool /* wakeupNode*/> mKernelWakeup;
 
 public:
     static constexpr int32_t DEFAULT_BATTERY = 1;
@@ -158,6 +160,8 @@
     void setMtSlotValues(int32_t deviceId, int32_t axis, const std::vector<int32_t>& values);
     base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
                                                        size_t slotCount) const override;
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override;
+    bool fakeReadKernelWakeup(int32_t deviceId) const;
 
 private:
     Device* getDevice(int32_t deviceId) const;
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 6c8b65c..19b738e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1392,6 +1392,20 @@
     ASSERT_EQ(mReader->getLightColor(deviceId, /*lightId=*/1), LIGHT_BRIGHTNESS);
 }
 
+TEST_F(InputReaderTest, SetPowerWakeUp) {
+    ASSERT_NO_FATAL_FAILURE(addDevice(1, "1st", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, "2nd", InputDeviceClass::KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(3, "3rd", InputDeviceClass::KEYBOARD, nullptr));
+
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(1), false);
+
+    ASSERT_TRUE(mFakeEventHub->setKernelWakeEnabled(2, true));
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(2), true);
+
+    ASSERT_TRUE(mFakeEventHub->setKernelWakeEnabled(3, false));
+    ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(3), false);
+}
+
 // --- InputReaderIntegrationTest ---
 
 // These tests create and interact with the InputReader only through its interface.
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index a43e4e4..25e2b45 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -180,6 +180,7 @@
     MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
     MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override));
     MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
+    MOCK_METHOD(bool, setKernelWakeEnabled, (int32_t deviceId, bool enabled), (override));
 };
 
 class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolicyInterface {
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 5442a65..64f3c27 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -171,6 +171,10 @@
 
     void notifyMouseCursorFadedOnTyping() override { reader->notifyMouseCursorFadedOnTyping(); }
 
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
+        return reader->setKernelWakeEnabled(deviceId, enabled);
+    }
+
 private:
     std::unique_ptr<InputReaderInterface> reader;
 };
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index fa8270a..60c676d 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -269,6 +269,9 @@
     status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
     status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
     void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
+    bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
+        return mFdp->ConsumeBool();
+    }
 };
 
 class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
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/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index eb6e677..26b5f4a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -33,7 +33,7 @@
 #include "DisplayHardware/HWC2.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -78,7 +78,7 @@
                  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>&));
+    MOCK_METHOD(Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1c18cd2..74ff124 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -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/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 334c104..c914ec3 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -240,7 +240,8 @@
                                                                  &mSessionConfig);
             if (ret.isOk()) {
                 mHintSession = ret.value();
-                if (FlagManager::getInstance().adpf_use_fmq_channel_fixed()) {
+                if (FlagManager::getInstance().adpf_use_fmq_channel_fixed() &&
+                    FlagManager::getInstance().adpf_fmq_sf()) {
                     setUpFmq();
                 }
             }
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/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/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index ab9014e..eca8df2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -722,13 +722,17 @@
             const bool inPrimaryPhysicalRange =
                     policy->primaryRanges.physical.includes(modePtr->getPeakFps());
             const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
-            if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+            if (!mIsVrrDevice.load() &&
+                ((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
                  !inPrimaryRenderRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
                    layer.vote == LayerVoteType::ExplicitExact))) {
                 // Only focused layers with ExplicitDefault frame rate settings are allowed to score
                 // refresh rates outside the primary range.
+                ALOGV("%s ignores %s (primaryRangeIsSingleRate). Current mode = %s",
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -852,7 +856,8 @@
                                    to_string(descending.front().frameRateMode.fps).c_str());
             return {descending, kNoSignals};
         } else {
-            ALOGV("primaryRangeIsSingleRate");
+            ALOGV("%s (primaryRangeIsSingleRate)",
+                  to_string(ranking.front().frameRateMode.fps).c_str());
             SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
                                    to_string(ranking.front().frameRateMode.fps).c_str());
             return {ranking, kNoSignals};
@@ -932,6 +937,8 @@
             using LayerVoteType = RefreshRateSelector::LayerVoteType;
 
             if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
+                ALOGV("%s: %s skips uid=%d due to the vote", __func__,
+                      formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid);
                 skipUid = true;
                 break;
             }
@@ -1014,12 +1021,14 @@
 
         // Layers with ExplicitExactOrMultiple expect touch boost
         if (globalSignals.touch && hasExplicitExactOrMultiple) {
+            ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid);
             continue;
         }
 
         // Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and
         // skip frame rate override.
         if (hasHighHint && !hasExplicitDefault) {
+            ALOGV("%s: Skipping for touch (HighHint): uid=%d", __func__, uid);
             continue;
         }
 
@@ -1043,6 +1052,9 @@
                 constexpr bool isSeamlessSwitch = true;
                 const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
                 score += layer->weight * layerScore;
+                ALOGV("%s: %s gives %s fps score of %.4f", __func__,
+                      formatLayerInfo(*layer, layer->weight).c_str(), to_string(fps).c_str(),
+                      layerScore);
             }
         }
 
@@ -1297,6 +1309,8 @@
     LOG_ALWAYS_FATAL_IF(!activeModeOpt);
     mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(),
                                    ftl::as_non_null(activeModeOpt->get())};
+    mIsVrrDevice = FlagManager::getInstance().vrr_config() &&
+            activeModeOpt->get()->getVrrConfig().has_value();
 
     const auto sortedModes = sortByRefreshRate(mDisplayModes);
     mMinRefreshRateModeIt = sortedModes.front();
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/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index e35346a..658bca6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -116,6 +116,7 @@
     DUMP_SERVER_FLAG(adpf_use_fmq_channel);
 
     /// Trunk stable readonly flags ///
+    DUMP_READ_ONLY_FLAG(adpf_fmq_sf);
     DUMP_READ_ONLY_FLAG(connected_display);
     DUMP_READ_ONLY_FLAG(enable_small_area_detection);
     DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
@@ -221,6 +222,7 @@
                                 "SkiaTracingFeature__use_skia_tracing")
 
 /// Trunk stable readonly flags ///
+FLAG_MANAGER_READ_ONLY_FLAG(adpf_fmq_sf, "")
 FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
 FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
 FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 5268ffa..4f34718 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -54,6 +54,7 @@
     bool adpf_use_fmq_channel_fixed() const;
 
     /// Trunk stable readonly flags ///
+    bool adpf_fmq_sf() const;
     bool connected_display() const;
     bool frame_rate_category_mrr() const;
     bool enable_small_area_detection() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 245bc2a..f758879 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -4,6 +4,14 @@
 container: "system"
 
 flag {
+  name: "adpf_fmq_sf"
+  namespace: "game"
+  description: "Guards use of the ADPF FMQ system specifically for SurfaceFlinger"
+  bug: "315894228"
+  is_fixed_read_only: true
+} # adpf_fmq_sf
+
+flag {
   name: "adpf_gpu_sf"
   namespace: "game"
   description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index c879280..8375bb9 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -85,6 +85,7 @@
     int64_t mSessionId = 123;
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, true);
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, false);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, false);
 };
 
 bool PowerAdvisorTest::sessionExists() {
@@ -184,6 +185,7 @@
     SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_gpu_sf,
                       config.adpfGpuFlagOn);
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, config.usesFmq);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, config.usesFmq);
     mPowerAdvisor->onBootFinished();
     bool expectsFmqSuccess = config.usesSharedFmqFlag && !config.fmqFull;
     if (config.usesFmq) {
@@ -789,6 +791,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(true, false);
     auto startTime = uptimeNanos();
@@ -807,6 +810,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint_noSharedFlag) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(false, false);
     SessionHint hint;
@@ -821,6 +825,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(true, true);
     ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index adbd868..29e1c21 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -308,6 +308,42 @@
                     << " category=" << ftl::enum_string(testCase.frameRateCategory);
         }
     }
+
+    template <class T>
+    std::vector<LayerRequirement> createLayers(const std::initializer_list<T>& surfaceVotes) {
+        std::vector<LayerRequirement> layers;
+        for (auto surfaceVote : surfaceVotes) {
+            ALOGI("**** %s: Adding layers for %s: (desiredFrameRate=%s, voteType=%s), "
+                  "(frameRateCategory=%s)",
+                  __func__, surfaceVote.name.c_str(),
+                  to_string(surfaceVote.desiredFrameRate).c_str(),
+                  ftl::enum_string(surfaceVote.voteType).c_str(),
+                  ftl::enum_string(surfaceVote.frameRateCategory).c_str());
+
+            if (surfaceVote.desiredFrameRate.isValid()) {
+                std::stringstream ss;
+                ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitDefault ("
+                   << to_string(surfaceVote.desiredFrameRate) << ")";
+                LayerRequirement layer = {.name = ss.str(),
+                                          .vote = surfaceVote.voteType,
+                                          .desiredRefreshRate = surfaceVote.desiredFrameRate,
+                                          .weight = surfaceVote.weight};
+                layers.push_back(layer);
+            }
+
+            if (surfaceVote.frameRateCategory != FrameRateCategory::Default) {
+                std::stringstream ss;
+                ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitCategory ("
+                   << ftl::enum_string(surfaceVote.frameRateCategory) << ")";
+                LayerRequirement layer = {.name = ss.str(),
+                                          .vote = LayerVoteType::ExplicitCategory,
+                                          .frameRateCategory = surfaceVote.frameRateCategory,
+                                          .weight = surfaceVote.weight};
+                layers.push_back(layer);
+            }
+        }
+        return layers;
+    }
 };
 
 RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1776,6 +1812,98 @@
             selector);
 }
 
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multiSurface_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    // Switch the policy to be more like an ARR device (primary range is a single rate).
+    constexpr FpsRange k120_120Hz = {120_Hz, 120_Hz};
+    constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+    constexpr FpsRanges kPrimaryRanges = {/*physical*/ k120_120Hz,
+                                          /*render*/ k120_120Hz};
+    constexpr FpsRanges kAppRequestRanges = {/*physical*/ k120_120Hz,
+                                             /*render*/ k0_120Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {/*defaultMode*/ kModeId120, kPrimaryRanges, kAppRequestRanges}));
+
+    // Surface can translate to multiple layers in SF  scheduler due to category and frame rate
+    // value.
+    struct SurfaceVote {
+        // Params
+        std::string name = "";
+        Fps desiredFrameRate = 0_Hz;
+        LayerVoteType voteType = LayerVoteType::ExplicitDefault;
+        FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+        float weight = 1.f;
+    };
+
+    auto layers = createLayers(
+            std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
+                                                .desiredFrameRate = 60_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .weight = 0.27f},
+                                               {.name = "1 fixed source + NoPreference",
+                                                .desiredFrameRate = 1_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .frameRateCategory =
+                                                        FrameRateCategory::NoPreference}});
+    auto actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(
+            std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
+                                                .desiredFrameRate = 60_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .weight = 0.27f},
+                                               {.name = "1 fixed source + Normal",
+                                                .desiredFrameRate = 1_Hz,
+                                                .voteType = LayerVoteType::ExplicitExactOrMultiple,
+                                                .frameRateCategory = FrameRateCategory::Normal}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "30 fixed source + NoPreference",
+             .desiredFrameRate = 30_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "1 fixed source + NoPreference",
+             .desiredFrameRate = 1_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    // Result affected by RefreshRateSelector.kMinSupportedFrameRate.
+    EXPECT_EQ(20_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "24 fixed source + NoPreference",
+             .desiredFrameRate = 24_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    EXPECT_EQ(24_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+
+    layers = createLayers(std::initializer_list<SurfaceVote>{
+            {.name = "23.976 fixed source + NoPreference",
+             .desiredFrameRate = 23.976_Hz,
+             .voteType = LayerVoteType::ExplicitExactOrMultiple,
+             .frameRateCategory = FrameRateCategory::NoPreference}});
+    actualRankedFrameRates = selector.getRankedFrameRates(layers);
+    // Chooses 120 unless certain threshold is set, see tests test23976Chooses120 and
+    // test23976Chooses60IfThresholdIs120.
+    EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+}
+
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
     auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
 
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..384b908 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -147,8 +147,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