Merge "Add component to the input tests presubmit module" into main am: 8ffdc4b563 am: c57676394a am: 503e3a6471 am: 9f028ebc9a am: a1a8c1e8f5 am: e850a1ba8a

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2653928

Change-Id: I0ba0d2820093d1e2d6a0540b6a5e7ed215e23cbe
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index f6e4ec3..4081514 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -291,6 +291,8 @@
         service = &(it->second);
 
         if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
+            LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name
+                         << "', but the service is not allowed for isolated apps.";
             return nullptr;
         }
         out = service->binder;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 226cae1..2143d93 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -263,6 +263,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.threadnetwork.prebuilt.xml",
+    src: "android.hardware.threadnetwork.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.usb.accessory.prebuilt.xml",
     src: "android.hardware.usb.accessory.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
index d36c958..5966cba 100644
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<!-- Feature for devices that support satellite communication via satellite vendor service APIs. -->
+<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
 <permissions>
     <feature name="android.hardware.telephony.satellite" />
 </permissions>
diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.threadnetwork.xml
new file mode 100644
index 0000000..9cbdc90
--- /dev/null
+++ b/data/etc/android.hardware.threadnetwork.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<!-- Adds the feature indicating support for the ThreadNetwork API -->
+<permissions>
+    <feature name="android.hardware.threadnetwork" />
+</permissions>
diff --git a/include/android/input.h b/include/android/input.h
index 9a0eb4d..16d86af 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -54,16 +54,12 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <android/keycodes.h>
-
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
 #include <android/looper.h>
 
 #include <jni.h>
 
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// definitions are provided.
 #if !defined(__INTRODUCED_IN)
 #define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
diff --git a/include/android/looper.h b/include/android/looper.h
index 4fe142a..e50730d 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -26,10 +26,18 @@
 #ifndef ANDROID_LOOPER_H
 #define ANDROID_LOOPER_H
 
+#include <sys/cdefs.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+// This file may also be built on glibc or on Windows/MacOS libc's, so
+// deprecated definitions are provided.
+#if !defined(__REMOVED_IN)
+#define __REMOVED_IN(__api_level) __attribute__((__deprecated__))
+#endif
+
 struct ALooper;
 /**
  * ALooper
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 16c5dde..a618393 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -29,6 +29,8 @@
 #ifndef ANDROID_SENSOR_H
 #define ANDROID_SENSOR_H
 
+#include <sys/cdefs.h>
+
 /******************************************************************
  *
  * IMPORTANT NOTICE:
@@ -45,11 +47,6 @@
  *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
  */
 
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
 #include <android/looper.h>
 
 #include <stdbool.h>
@@ -57,6 +54,8 @@
 #include <math.h>
 #include <stdint.h>
 
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// and deprecated definitions are provided.
 #if !defined(__INTRODUCED_IN)
 #define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
@@ -658,7 +657,7 @@
     uint32_t flags;
     int32_t reserved1[3];
 } ASensorEvent;
-// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
+// LINT.ThenChange(hardware/libhardware/include/hardware/sensors.h)
 
 struct ASensorManager;
 /**
diff --git a/include/input/Input.h b/include/input/Input.h
index 527a477..a271f0c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -282,6 +282,7 @@
 
     // Indicates that the key represents a special gesture that has been detected by
     // the touch firmware or driver.  Causes touch events from the same device to be canceled.
+    // This policy flag prevents key events from changing touch mode state.
     POLICY_FLAG_GESTURE = 0x00000008,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
@@ -492,14 +493,17 @@
         toolType = ToolType::UNKNOWN;
     }
 
-    bool operator==(const PointerProperties& other) const;
+    bool operator==(const PointerProperties& other) const = default;
     inline bool operator!=(const PointerProperties& other) const {
         return !(*this == other);
     }
 
-    void copyFrom(const PointerProperties& other);
+    PointerProperties& operator=(const PointerProperties&) = default;
 };
 
+// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead
+using DeviceId = int32_t;
+
 /*
  * Input events.
  */
@@ -511,7 +515,7 @@
 
     inline int32_t getId() const { return mId; }
 
-    inline int32_t getDeviceId() const { return mDeviceId; }
+    inline DeviceId getDeviceId() const { return mDeviceId; }
 
     inline uint32_t getSource() const { return mSource; }
 
@@ -526,13 +530,13 @@
     static int32_t nextId();
 
 protected:
-    void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+    void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac);
 
     void initialize(const InputEvent& from);
 
     int32_t mId;
-    int32_t mDeviceId;
+    DeviceId mDeviceId;
     uint32_t mSource;
     int32_t mDisplayId;
     std::array<uint8_t, 32> mHmac;
@@ -570,7 +574,7 @@
     static const char* getLabel(int32_t keyCode);
     static std::optional<int> getKeyCodeFromLabel(const char* label);
 
-    void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+    void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
                     int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                     nsecs_t eventTime);
@@ -834,7 +838,7 @@
 
     ssize_t findPointerIndex(int32_t pointerId) const;
 
-    void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+    void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
                     int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
                     MotionClassification classification, const ui::Transform& transform,
@@ -1012,7 +1016,7 @@
     };
 
     Type type;
-    int32_t deviceId;
+    DeviceId deviceId;
     nsecs_t eventTimeNanos;
     uint32_t source;
     int32_t displayId;
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index 3715408..b857482 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -51,6 +51,8 @@
                                                 const PointerProperties* pointerProperties,
                                                 const PointerCoords* pointerCoords, int32_t flags);
 
+    void resetDevice(int32_t deviceId);
+
 private:
     rust::Box<android::input::verifier::InputVerifier> mVerifier;
 };
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 8c3c74a..b126abe 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,13 +17,13 @@
 #pragma once
 
 #include <android-base/result.h>
+#include <input/InputDevice.h>
+
 #include <stdint.h>
 #include <utils/Errors.h>
 #include <utils/Tokenizer.h>
 #include <set>
 
-#include <input/InputDevice.h>
-
 namespace android {
 
 struct AxisInfo {
diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h
index 37fe5af..d2747d6 100644
--- a/include/input/RingBuffer.h
+++ b/include/input/RingBuffer.h
@@ -24,7 +24,6 @@
 #include <type_traits>
 #include <utility>
 
-#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
 namespace android {
@@ -277,15 +276,16 @@
 
     // Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
     size_type bufferIndex(size_type elementIndex) const {
-        CHECK_LE(elementIndex, size());
+        if (elementIndex > size()) {
+            abort();
+        }
         size_type index = mBegin + elementIndex;
         if (index >= capacity()) {
             index -= capacity();
         }
-        CHECK_LT(index, capacity())
-                << android::base::StringPrintf("Invalid index calculated for element (%zu) "
-                                               "in buffer of size %zu",
-                                               elementIndex, size());
+        if (index >= capacity()) {
+            abort();
+        }
         return index;
     }
 
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index f3c201e..b78f63e 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -88,7 +88,7 @@
     VelocityControl();
 
     /* Gets the various parameters. */
-    VelocityControlParameters& getParameters();
+    const VelocityControlParameters& getParameters() const;
 
     /* Sets the various parameters. */
     void setParameters(const VelocityControlParameters& parameters);
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index da97c3e..b58feac 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <input/Input.h>
+#include <input/RingBuffer.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
 #include <map>
@@ -31,6 +32,8 @@
  */
 class VelocityTracker {
 public:
+    static const size_t MAX_DEGREE = 4;
+
     enum class Strategy : int32_t {
         DEFAULT = -1,
         MIN = 0,
@@ -47,23 +50,6 @@
         MAX = LEGACY,
     };
 
-    struct Estimator {
-        static const size_t MAX_DEGREE = 4;
-
-        // Estimator time base.
-        nsecs_t time = 0;
-
-        // Polynomial coefficients describing motion.
-        std::array<float, MAX_DEGREE + 1> coeff{};
-
-        // Polynomial degree (number of coefficients), or zero if no information is
-        // available.
-        uint32_t degree = 0;
-
-        // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
-        float confidence = 0;
-    };
-
     /*
      * Contains all available velocity data from a VelocityTracker.
      */
@@ -124,11 +110,6 @@
     // [-maxVelocity, maxVelocity], inclusive.
     ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
 
-    // Gets an estimator for the recent movements of the specified pointer id for the given axis.
-    // Returns false and clears the estimator if there is no information available
-    // about the pointer.
-    std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const;
-
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); }
 
@@ -169,14 +150,48 @@
 
     virtual void clearPointer(int32_t pointerId) = 0;
     virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0;
-    virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0;
+    virtual std::optional<float> getVelocity(int32_t pointerId) const = 0;
 };
 
+/**
+ * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data
+ * points when getting velocity.
+ */
+class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy {
+public:
+    AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd);
+
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+    void clearPointer(int32_t pointerId) override;
+
+protected:
+    struct Movement {
+        nsecs_t eventTime;
+        float position;
+    };
+
+    // Number of samples to keep.
+    // If different strategies would like to maintain different history size, we can make this a
+    // protected const field.
+    static constexpr uint32_t HISTORY_SIZE = 20;
+
+    /**
+     * Duration, in nanoseconds, since the latest movement where a movement may be considered for
+     * velocity calculation.
+     */
+    const nsecs_t mHorizonNanos;
+    /**
+     * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each
+     * addition of a new movement.
+     */
+    const bool mMaintainHorizonDuringAdd;
+    std::map<int32_t /*pointerId*/, RingBuffer<Movement>> mMovements;
+};
 
 /*
  * Velocity tracker algorithm based on least-squares linear regression.
  */
-class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     enum class Weighting {
         // No weights applied.  All data points are equally reliable.
@@ -193,13 +208,11 @@
         RECENT,
     };
 
-    // Degree must be no greater than Estimator::MAX_DEGREE.
+    // Degree must be no greater than VelocityTracker::MAX_DEGREE.
     LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE);
     ~LeastSquaresVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -207,23 +220,19 @@
     // changes in direction.
     static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static const uint32_t HISTORY_SIZE = 20;
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
     float chooseWeight(int32_t pointerId, uint32_t index) const;
+    /**
+     * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`).
+     * The provided container of movements shall NOT be empty, and shall have the movements in
+     * chronological order.
+     */
+    std::optional<float> solveUnweightedLeastSquaresDeg2(
+            const RingBuffer<Movement>& movements) const;
 
     const uint32_t mDegree;
     const Weighting mWeighting;
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
-
 /*
  * Velocity tracker algorithm that uses an IIR filter.
  */
@@ -235,7 +244,7 @@
 
     void clearPointer(int32_t pointerId) override;
     void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Current state estimate for a particular pointer.
@@ -252,49 +261,33 @@
 
     void initState(State& state, nsecs_t eventTime, float pos) const;
     void updateState(State& state, nsecs_t eventTime, float pos) const;
-    void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
 };
 
 
 /*
  * Velocity tracker strategy used prior to ICS.
  */
-class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     LegacyVelocityTrackerStrategy();
     ~LegacyVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Oldest sample to consider when calculating the velocity.
     static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static const uint32_t HISTORY_SIZE = 20;
-
     // The minimum duration between samples when estimating velocity.
     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
-class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
 public:
     ImpulseVelocityTrackerStrategy(bool deltaValues);
     ~ImpulseVelocityTrackerStrategy() override;
 
-    void clearPointer(int32_t pointerId) override;
-    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
-    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+    std::optional<float> getVelocity(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -302,21 +295,10 @@
     // changes in direction.
     static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms
 
-    // Number of samples to keep.
-    static constexpr size_t HISTORY_SIZE = 20;
-
-    struct Movement {
-        nsecs_t eventTime;
-        float position;
-    };
-
     // Whether or not the input movement values for the strategy come in the form of delta values.
     // If the input values are not deltas, the strategy needs to calculate deltas as part of its
     // velocity calculation.
     const bool mDeltaValues;
-
-    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
-    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
 } // namespace android
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 82e4551..9e426d3 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -17,11 +17,11 @@
 #ifndef ANDROID_POWERHALCONTROLLER_H
 #define ANDROID_POWERHALCONTROLLER_H
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android-base/thread_annotations.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
 #include <powermanager/PowerHalWrapper.h>
 
 namespace android {
@@ -55,11 +55,13 @@
 
     virtual void init();
 
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
-            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-            int64_t durationNanos) override;
+    virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                                     int32_t durationMs) override;
+    virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
+                                    bool enabled) override;
+    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+    createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+                      int64_t durationNanos) override;
     virtual HalResult<int64_t> getHintSessionPreferredRate() override;
 
 private:
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index e0384f3..cbbfa59 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -17,11 +17,11 @@
 #ifndef ANDROID_POWERHALLOADER_H
 #define ANDROID_POWERHALLOADER_H
 
+#include <aidl/android/hardware/power/IPower.h>
 #include <android-base/thread_annotations.h>
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/1.2/IPower.h>
 #include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
 
 namespace android {
 
@@ -31,7 +31,7 @@
 class PowerHalLoader {
 public:
     static void unloadAll();
-    static sp<hardware::power::IPower> loadAidl();
+    static std::shared_ptr<aidl::android::hardware::power::IPower> loadAidl();
     static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
     static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
     static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
@@ -39,7 +39,7 @@
 
 private:
     static std::mutex gHalMutex;
-    static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+    static std::shared_ptr<aidl::android::hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
     static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
     static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
     static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 8028aa8..4e4a1b0 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -17,14 +17,15 @@
 #ifndef ANDROID_POWERHALWRAPPER_H
 #define ANDROID_POWERHALWRAPPER_H
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android-base/thread_annotations.h>
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/1.2/IPower.h>
 #include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <binder/Status.h>
 
 namespace android {
 
@@ -47,7 +48,7 @@
     }
     static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
 
-    static HalResult<T> fromStatus(binder::Status status, T data) {
+    static HalResult<T> fromStatus(const binder::Status& status, T data) {
         if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
             return HalResult<T>::unsupported();
         }
@@ -56,14 +57,28 @@
         }
         return HalResult<T>::failed(std::string(status.toString8().c_str()));
     }
-    static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+    static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) {
+        if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+            return HalResult<T>::unsupported();
+        }
+        if (status.isOk()) {
+            return HalResult<T>::ok(data);
+        }
+        return HalResult<T>::failed(std::string(status.getDescription()));
+    }
 
     template <typename R>
-    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) {
+        return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+    }
 
     template <typename R>
     static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
-                                   T data);
+                                   T data) {
+        return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+                          : HalResult<T>::failed(ret.description());
+    }
 
     // This will throw std::bad_optional_access if this result is not ok.
     const T& value() const { return mValue.value(); }
@@ -91,12 +106,30 @@
     static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
     static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
 
-    static HalResult<void> fromStatus(status_t status);
-    static HalResult<void> fromStatus(binder::Status status);
-    static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+    static HalResult<void> fromStatus(const binder::Status& status) {
+        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+            return HalResult<void>::unsupported();
+        }
+        if (status.isOk()) {
+            return HalResult<void>::ok();
+        }
+        return HalResult<void>::failed(std::string(status.toString8().c_str()));
+    }
+
+    static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
+        if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+            return HalResult<void>::unsupported();
+        }
+        if (status.isOk()) {
+            return HalResult<void>::ok();
+        }
+        return HalResult<void>::failed(std::string(status.getDescription()));
+    }
 
     template <typename R>
-    static HalResult<void> fromReturn(hardware::Return<R>& ret);
+    static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+        return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+    }
 
     bool isOk() const { return !mUnsupported && !mFailed; }
     bool isFailed() const { return !mUnsupported && mFailed; }
@@ -119,11 +152,12 @@
 public:
     virtual ~HalWrapper() = default;
 
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
-    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
-            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-            int64_t durationNanos) = 0;
+    virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                                     int32_t durationMs) = 0;
+    virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
+    virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+    createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+                      int64_t durationNanos) = 0;
     virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
 };
 
@@ -131,14 +165,15 @@
 class EmptyHalWrapper : public HalWrapper {
 public:
     EmptyHalWrapper() = default;
-    ~EmptyHalWrapper() = default;
+    ~EmptyHalWrapper() override = default;
 
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                             int32_t durationMs) override;
+    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) override;
-    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+    HalResult<int64_t> getHintSessionPreferredRate() override;
 };
 
 // Wrapper for the HIDL Power HAL v1.0.
@@ -146,14 +181,15 @@
 public:
     explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
           : mHandleV1_0(std::move(handleV1_0)) {}
-    virtual ~HidlHalWrapperV1_0() = default;
+    ~HidlHalWrapperV1_0() override = default;
 
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                             int32_t durationMs) override;
+    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) override;
-    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+    HalResult<int64_t> getHintSessionPreferredRate() override;
 
 protected:
     const sp<hardware::power::V1_0::IPower> mHandleV1_0;
@@ -167,67 +203,71 @@
 // Wrapper for the HIDL Power HAL v1.1.
 class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
 public:
-    HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
+    explicit HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
           : HidlHalWrapperV1_0(std::move(handleV1_1)) {}
-    virtual ~HidlHalWrapperV1_1() = default;
+    ~HidlHalWrapperV1_1() override = default;
 
 protected:
-    virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
-                                          uint32_t data) override;
+    HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
 };
 
 // Wrapper for the HIDL Power HAL v1.2.
 class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
 public:
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
+    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                             int32_t durationMs) override;
+    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
           : HidlHalWrapperV1_1(std::move(handleV1_2)) {}
-    virtual ~HidlHalWrapperV1_2() = default;
+    ~HidlHalWrapperV1_2() override = default;
 
 protected:
-    virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
-                                          uint32_t data) override;
+    HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
 };
 
 // Wrapper for the HIDL Power HAL v1.3.
 class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
 public:
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
+    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
           : HidlHalWrapperV1_2(std::move(handleV1_3)) {}
-    virtual ~HidlHalWrapperV1_3() = default;
+    ~HidlHalWrapperV1_3() override = default;
 
 protected:
-    virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
-                                          uint32_t data) override;
+    HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
 };
 
 // Wrapper for the AIDL Power HAL.
 class AidlHalWrapper : public HalWrapper {
 public:
-    explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
-    virtual ~AidlHalWrapper() = default;
+    explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle)
+          : mHandle(std::move(handle)) {}
+    ~AidlHalWrapper() override = default;
 
-    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
-    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+    HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+                             int32_t durationMs) override;
+    HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+    HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
             int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
             int64_t durationNanos) override;
-    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+    HalResult<int64_t> getHintSessionPreferredRate() override;
 
 private:
     // Control access to the boost and mode supported arrays.
     std::mutex mBoostMutex;
     std::mutex mModeMutex;
-    sp<hardware::power::IPower> mHandle;
+    std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
     // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
     // Need to increase the array size if more boost supported.
-    std::array<std::atomic<HalSupport>,
-               static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+    std::array<
+            std::atomic<HalSupport>,
+            static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
+                    1>
             mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
     std::array<std::atomic<HalSupport>,
-               static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1>
+               static_cast<int32_t>(
+                       *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
+                       1>
             mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
 };
 
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index aca5009..5264276 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -21,6 +21,7 @@
 #include <binder/ActivityManager.h>
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 
 #include <utils/SystemClock.h>
 
@@ -33,27 +34,36 @@
 sp<IActivityManager> ActivityManager::getService()
 {
     std::lock_guard<Mutex> scoped_lock(mLock);
-    int64_t startTime = 0;
     sp<IActivityManager> service = mService;
-    while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
-        sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
-        if (binder == nullptr) {
-            // Wait for the activity service to come back...
-            if (startTime == 0) {
-                startTime = uptimeMillis();
-                ALOGI("Waiting for activity service");
-            } else if ((uptimeMillis() - startTime) > 1000000) {
-                ALOGW("Waiting too long for activity service, giving up");
-                service = nullptr;
-                break;
-            }
-            usleep(25000);
-        } else {
+    if (ProcessState::self()->isThreadPoolStarted()) {
+        if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+            sp<IBinder> binder = defaultServiceManager()->waitForService(String16("activity"));
             service = interface_cast<IActivityManager>(binder);
             mService = service;
         }
+    } else {
+        ALOGI("Thread pool not started. Polling for activity service.");
+        int64_t startTime = 0;
+        while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+            sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
+            if (binder == nullptr) {
+                // Wait for the activity service to come back...
+                if (startTime == 0) {
+                    startTime = uptimeMillis();
+                    ALOGI("Waiting for activity service");
+                } else if ((uptimeMillis() - startTime) > 1000000) {
+                    ALOGW("Waiting too long for activity service, giving up");
+                    service = nullptr;
+                    break;
+                }
+                usleep(25000);
+            } else {
+                service = interface_cast<IActivityManager>(binder);
+                mService = service;
+            }
+        }
     }
-    return service;
+    return mService;
 }
 
 int ActivityManager::openContentUri(const String16& stringUri)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9b685f9..bbaa419 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -69,6 +69,10 @@
 typedef uintptr_t binder_uintptr_t;
 #endif // BINDER_WITH_KERNEL_IPC
 
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
 #define LOG_REFS(...)
 // #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 #define LOG_ALLOC(...)
@@ -109,6 +113,37 @@
 // Maximum size of a blob to transfer in-place.
 static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
 
+#if defined(__BIONIC__)
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+    if (android_fdsan_exchange_owner_tag) {
+        uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+                                                          reinterpret_cast<uint64_t>(old_addr));
+        uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+                                                          reinterpret_cast<uint64_t>(new_addr));
+        android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+    }
+}
+static void FdTagClose(int fd, const void* addr) {
+    if (android_fdsan_close_with_tag) {
+        uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+                                                      reinterpret_cast<uint64_t>(addr));
+        android_fdsan_close_with_tag(fd, tag);
+    } else {
+        close(fd);
+    }
+}
+#else
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+    (void)fd;
+    (void)old_addr;
+    (void)new_addr;
+}
+static void FdTagClose(int fd, const void* addr) {
+    (void)addr;
+    close(fd);
+}
+#endif
+
 enum {
     BLOB_INPLACE = 0,
     BLOB_ASHMEM_IMMUTABLE = 1,
@@ -134,6 +169,9 @@
             return;
         }
         case BINDER_TYPE_FD: {
+            if (obj.cookie != 0) { // owned
+                FdTag(obj.handle, nullptr, who);
+            }
             return;
         }
     }
@@ -159,8 +197,10 @@
             return;
         }
         case BINDER_TYPE_FD: {
+            // note: this path is not used when mOwner, so the tag is also released
+            // in 'closeFileDescriptors'
             if (obj.cookie != 0) { // owned
-                close(obj.handle);
+                FdTagClose(obj.handle, who);
             }
             return;
         }
@@ -554,7 +594,6 @@
                 kernelFields->mObjectsSize++;
 
                 flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off);
-                acquire_object(proc, *flat, this);
 
                 if (flat->hdr.type == BINDER_TYPE_FD) {
                     // If this is a file descriptor, we need to dup it so the
@@ -567,6 +606,8 @@
                         err = FDS_NOT_ALLOWED;
                     }
                 }
+
+                acquire_object(proc, *flat, this);
             }
         }
 #else
@@ -2553,7 +2594,8 @@
                     reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
             if (flat->hdr.type == BINDER_TYPE_FD) {
                 // ALOGI("Closing fd: %ld", flat->handle);
-                close(flat->handle);
+                // FDs from the kernel are always owned
+                FdTagClose(flat->handle, this);
             }
         }
 #else  // BINDER_WITH_KERNEL_IPC
@@ -2634,6 +2676,10 @@
             kernelFields->mObjectsSize = 0;
             break;
         }
+        if (type == BINDER_TYPE_FD) {
+            // FDs from the kernel are always owned
+            FdTag(flat->handle, 0, this);
+        }
         minOffset = offset + sizeof(flat_binder_object);
     }
     scanForFds();
diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp
new file mode 100644
index 0000000..e1dc9ba
--- /dev/null
+++ b/libs/bufferstreams/Android.bp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
diff --git a/libs/bufferstreams/OWNERS b/libs/bufferstreams/OWNERS
new file mode 100644
index 0000000..32b72b8
--- /dev/null
+++ b/libs/bufferstreams/OWNERS
@@ -0,0 +1,7 @@
+carlosmr@google.com
+hibrian@google.com
+jreck@google.com
+jshargo@google.com
+
+file:/services/surfaceflinger/OWNERS
+
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 6849a95..67cbc7b 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -99,7 +99,7 @@
     if (mEventConnection != nullptr) {
         auto status = mEventConnection->getLatestVsyncEventData(outVsyncEventData);
         if (!status.isOk()) {
-            ALOGE("Failed to get latest vsync event data: %s", status.exceptionMessage().c_str());
+            ALOGE("Failed to get latest vsync event data: %s", status.toString8().c_str());
             return status.transactionError();
         }
         return NO_ERROR;
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 82e1b5a..75bae76 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -46,7 +46,7 @@
         "android.hardware.configstore-utils",
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
         "android.hidl.token@1.0",
         "libSurfaceFlingerProp",
         "libgui",
@@ -72,6 +72,14 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 155276,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libgui library",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 95b7f39..3e37e48 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
 #include <fuzzbinder/libbinder_driver.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -39,10 +39,13 @@
                                          ui::ColorMode::BT2100_HLG,
                                          ui::ColorMode::DISPLAY_BT2020};
 
-constexpr hardware::power::Boost kBoost[] = {
-        hardware::power::Boost::INTERACTION,   hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
-        hardware::power::Boost::ML_ACC,        hardware::power::Boost::AUDIO_LAUNCH,
-        hardware::power::Boost::CAMERA_LAUNCH, hardware::power::Boost::CAMERA_SHOT,
+constexpr aidl::android::hardware::power::Boost kBoost[] = {
+        aidl::android::hardware::power::Boost::INTERACTION,
+        aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
+        aidl::android::hardware::power::Boost::ML_ACC,
+        aidl::android::hardware::power::Boost::AUDIO_LAUNCH,
+        aidl::android::hardware::power::Boost::CAMERA_LAUNCH,
+        aidl::android::hardware::power::Boost::CAMERA_SHOT,
 };
 
 constexpr gui::TouchOcclusionMode kMode[] = {
@@ -284,7 +287,7 @@
     SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>());
 
     SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp));
-    hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
+    aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
     SurfaceComposerClient::notifyPowerBoost((int32_t)boostId);
 
     String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 1dddeba..bf354e7 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -46,6 +46,8 @@
     // where the previous frame was presented in the current frame's expected vsync. This pushes the
     // current frame to the next vsync. The behavior is similar to BufferStuffing.
     SurfaceFlingerStuffing = 0x100,
+    // Frame was dropped, as a newer frame was ready and replaced this frame.
+    Dropped = 0x200,
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 62e5f89..7aa7068 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -267,7 +267,8 @@
             layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
             layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
             layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
-            layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
+            layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateSelectionPriority |
+            layer_state_t::eFixedTransformHintChanged;
 
     // Changes affecting data sent to input.
     static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged |
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8a17d8a..769677c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -109,62 +109,6 @@
     ],
 }
 
-genrule {
-    name: "libinput_cxx_bridge_code",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) >> $(out)",
-    srcs: ["input_verifier.rs"],
-    out: ["inputverifier_generated.cpp"],
-}
-
-genrule {
-    name: "libinput_cxx_bridge_header",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
-    srcs: ["input_verifier.rs"],
-    out: ["input_verifier.rs.h"],
-}
-
-rust_defaults {
-    name: "libinput_rust_defaults",
-    srcs: ["input_verifier.rs"],
-    host_supported: true,
-    rustlibs: [
-        "libbitflags",
-        "libcxx",
-        "libinput_bindgen",
-        "liblogger",
-        "liblog_rust",
-        "inputconstants-rust",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-}
-
-rust_ffi_static {
-    name: "libinput_rust",
-    crate_name: "input",
-    defaults: ["libinput_rust_defaults"],
-}
-
-rust_test {
-    name: "libinput_rust_test",
-    defaults: ["libinput_rust_defaults"],
-    whole_static_libs: [
-        "libinput_from_rust_to_cpp",
-    ],
-    test_options: {
-        unit_test: true,
-    },
-    test_suites: ["device_tests"],
-    sanitize: {
-        hwaddress: true,
-    },
-}
-
 cc_library {
     name: "libinput",
     cpp_std: "c++20",
@@ -174,12 +118,18 @@
         "-Wextra",
         "-Werror",
         "-Wno-unused-parameter",
+        "-Wthread-safety",
+        "-Wshadow",
+        "-Wshadow-field-in-constructor-modified",
+        "-Wshadow-uncaptured-local",
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
     srcs: [
-        "FromRustToCpp.cpp",
+        "android/os/IInputFlinger.aidl",
         "Input.cpp",
         "InputDevice.cpp",
         "InputEventLabels.cpp",
+        "InputTransport.cpp",
         "InputVerifier.cpp",
         "Keyboard.cpp",
         "KeyCharacterMap.cpp",
@@ -208,14 +158,14 @@
         "toolbox_input_labels",
     ],
 
-    generated_sources: ["libinput_cxx_bridge_code"],
-
     shared_libs: [
         "libbase",
+        "libbinder",
         "libcutils",
         "liblog",
         "libPlatformProperties",
         "libtinyxml2",
+        "libutils",
         "libvintf",
     ],
 
@@ -231,15 +181,17 @@
 
     static_libs: [
         "inputconstants-cpp",
+        "libgui_window_info_static",
         "libui-types",
         "libtflite_static",
     ],
 
     whole_static_libs: [
-        "libinput_rust",
+        "libinput_rust_ffi",
     ],
 
     export_static_lib_headers: [
+        "libgui_window_info_static",
         "libui-types",
     ],
 
@@ -250,54 +202,15 @@
 
     target: {
         android: {
-            srcs: [
-                "InputTransport.cpp",
-                "android/os/IInputFlinger.aidl",
-            ],
-
-            export_shared_lib_headers: ["libbinder"],
-
-            shared_libs: [
-                "libutils",
-                "libbinder",
-            ],
-
-            static_libs: [
-                "libgui_window_info_static",
-            ],
-
-            export_static_lib_headers: [
-                "libgui_window_info_static",
-            ],
-
             required: [
                 "motion_predictor_model_prebuilt",
                 "motion_predictor_model_config",
             ],
         },
         host: {
-            shared: {
-                enabled: false,
-            },
             include_dirs: [
                 "bionic/libc/kernel/android/uapi/",
                 "bionic/libc/kernel/uapi",
-                "frameworks/native/libs/arect/include",
-            ],
-        },
-        host_linux: {
-            srcs: [
-                "InputTransport.cpp",
-            ],
-            static_libs: [
-                "libgui_window_info_static",
-            ],
-            shared_libs: [
-                "libbinder",
-            ],
-
-            export_static_lib_headers: [
-                "libgui_window_info_static",
             ],
         },
     },
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 00925ba..c127411 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -497,19 +497,6 @@
     }
 }
 
-// --- PointerProperties ---
-
-bool PointerProperties::operator==(const PointerProperties& other) const {
-    return id == other.id
-            && toolType == other.toolType;
-}
-
-void PointerProperties::copyFrom(const PointerProperties& other) {
-    id = other.id;
-    toolType = other.toolType;
-}
-
-
 // --- MotionEvent ---
 
 void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 4d3d8bc..d9b7700 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -409,7 +409,7 @@
     setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
     setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
 
-    sp<IBinder> token = new BBinder();
+    sp<IBinder> token = sp<BBinder>::make();
 
     std::string serverChannelName = name + " (server)";
     android::base::unique_fd serverFd(sockets[0]);
@@ -692,8 +692,8 @@
     msg.body.motion.eventTime = eventTime;
     msg.body.motion.pointerCount = pointerCount;
     for (uint32_t i = 0; i < pointerCount; i++) {
-        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
-        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
+        msg.body.motion.pointers[i].properties = pointerProperties[i];
+        msg.body.motion.pointers[i].coords = pointerCoords[i];
     }
 
     return mChannel->sendMessage(&msg);
@@ -1270,13 +1270,13 @@
             // We know here that the coordinates for the pointer haven't changed because we
             // would've cleared the resampled bit in rewriteMessage if they had. We can't modify
             // lastResample in place becasue the mapping from pointer ID to index may have changed.
-            touchState.lastResample.pointers[i].copyFrom(oldLastResample.getPointerById(id));
+            touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id);
             continue;
         }
 
         PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
         const PointerCoords& currentCoords = current->getPointerById(id);
-        resampledCoords.copyFrom(currentCoords);
+        resampledCoords = currentCoords;
         if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
             const PointerCoords& otherCoords = other->getPointerById(id);
             resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
@@ -1454,8 +1454,8 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
     for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
-        pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+        pointerProperties[i] = msg->body.motion.pointers[i].properties;
+        pointerCoords[i] = msg->body.motion.pointers[i].coords;
     }
 
     ui::Transform transform;
@@ -1484,7 +1484,7 @@
     uint32_t pointerCount = msg->body.motion.pointerCount;
     PointerCoords pointerCoords[pointerCount];
     for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+        pointerCoords[i] = msg->body.motion.pointers[i].coords;
     }
 
     event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 32b4ca0..851babf 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -18,12 +18,14 @@
 
 #include <android-base/logging.h>
 #include <input/InputVerifier.h>
-#include "input_verifier.rs.h"
+#include "input_cxx_bridge.rs.h"
 
 using android::base::Error;
 using android::base::Result;
 using android::input::RustPointerProperties;
 
+using DeviceId = int32_t;
+
 namespace android {
 
 // --- InputVerifier ---
@@ -31,7 +33,8 @@
 InputVerifier::InputVerifier(const std::string& name)
       : mVerifier(android::input::verifier::create(name)){};
 
-Result<void> InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+                                            uint32_t pointerCount,
                                             const PointerProperties* pointerProperties,
                                             const PointerCoords* pointerCoords, int32_t flags) {
     std::vector<RustPointerProperties> rpp;
@@ -49,4 +52,8 @@
     }
 }
 
+void InputVerifier::resetDevice(DeviceId deviceId) {
+    android::input::verifier::reset_device(*mVerifier, deviceId);
+}
+
 } // namespace android
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 68e6888..80a8d03 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -25,9 +25,9 @@
 #include <string>
 #include <vector>
 
+#include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <android/input.h>
-#include <log/log.h>
 
 #include <attestation/HmacKeyManager.h>
 #include <ftl/enum.h>
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 5720099..c835a08 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -37,7 +37,7 @@
     reset();
 }
 
-VelocityControlParameters& VelocityControl::getParameters() {
+const VelocityControlParameters& VelocityControl::getParameters() const{
     return mParameters;
 }
 
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8551e5f..87c7768 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -22,7 +22,6 @@
 #include <math.h>
 #include <optional>
 
-#include <android-base/stringprintf.h>
 #include <input/PrintTools.h>
 #include <input/VelocityTracker.h>
 #include <utils/BitSet.h>
@@ -56,6 +55,9 @@
 // Nanoseconds per milliseconds.
 static const nsecs_t NANOS_PER_MS = 1000000;
 
+// Seconds per nanosecond.
+static const float SECONDS_PER_NANO = 1E-9;
+
 // All axes supported for velocity tracking, mapped to their default strategies.
 // Although other strategies are available for testing and comparison purposes,
 // the default strategy is the one that applications will actually use.  Be very careful
@@ -268,12 +270,8 @@
               ", activePointerId=%s",
               eventTime, pointerId, toString(mActivePointerId).c_str());
 
-        std::optional<Estimator> estimator = getEstimator(axis, pointerId);
-        ALOGD("  %d: axis=%d, position=%0.3f, "
-              "estimator (degree=%d, coeff=%s, confidence=%f)",
-              pointerId, axis, position, int((*estimator).degree),
-              vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(),
-              (*estimator).confidence);
+        ALOGD("  %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
+              toString(getVelocity(axis, pointerId)).c_str());
     }
 }
 
@@ -349,9 +347,9 @@
 }
 
 std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const {
-    std::optional<Estimator> estimator = getEstimator(axis, pointerId);
-    if (estimator && (*estimator).degree >= 1) {
-        return (*estimator).coeff[1];
+    const auto& it = mConfiguredStrategies.find(axis);
+    if (it != mConfiguredStrategies.end()) {
+        return it->second->getVelocity(pointerId);
     }
     return {};
 }
@@ -374,56 +372,52 @@
     return computedVelocity;
 }
 
-std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis,
-                                                                        int32_t pointerId) const {
-    const auto& it = mConfiguredStrategies.find(axis);
-    if (it == mConfiguredStrategies.end()) {
-        return std::nullopt;
+AccumulatingVelocityTrackerStrategy::AccumulatingVelocityTrackerStrategy(
+        nsecs_t horizonNanos, bool maintainHorizonDuringAdd)
+      : mHorizonNanos(horizonNanos), mMaintainHorizonDuringAdd(maintainHorizonDuringAdd) {}
+
+void AccumulatingVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mMovements.erase(pointerId);
+}
+
+void AccumulatingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                      float position) {
+    auto [ringBufferIt, _] = mMovements.try_emplace(pointerId, HISTORY_SIZE);
+    RingBuffer<Movement>& movements = ringBufferIt->second;
+    const size_t size = movements.size();
+
+    if (size != 0 && movements[size - 1].eventTime == eventTime) {
+        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
+        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
+        // the new pointer. If the eventtimes for both events are identical, just update the data
+        // for this time (i.e. pop out the last element, and insert the updated movement).
+        // We only compare against the last value, as it is likely that addMovement is called
+        // in chronological order as events occur.
+        movements.popBack();
     }
-    return it->second->getEstimator(pointerId);
+
+    movements.pushBack({eventTime, position});
+
+    // Clear movements that do not fall within `mHorizonNanos` of the latest movement.
+    // Note that, if in the future we decide to use more movements (i.e. increase HISTORY_SIZE),
+    // we can consider making this step binary-search based, which will give us some improvement.
+    if (mMaintainHorizonDuringAdd) {
+        while (eventTime - movements[0].eventTime > mHorizonNanos) {
+            movements.popFront();
+        }
+    }
 }
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree,
                                                                          Weighting weighting)
-      : mDegree(degree), mWeighting(weighting) {}
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            true /*maintainHorizonDuringAdd*/),
+        mDegree(degree),
+        mWeighting(weighting) {}
 
-LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
-}
-
-void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                      float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
+LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
 
 /**
  * Solves a linear least squares problem to obtain a N degree polynomial that fits
@@ -474,10 +468,9 @@
  * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
  * http://en.wikipedia.org/wiki/Gram-Schmidt
  */
-static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
-                              const std::vector<float>& w, uint32_t n,
-                              std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB,
-                              float* outDet) {
+static std::optional<float> solveLeastSquares(const std::vector<float>& x,
+                                              const std::vector<float>& y,
+                                              const std::vector<float>& w, uint32_t n) {
     const size_t m = x.size();
 
     ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
@@ -515,7 +508,7 @@
         if (norm < 0.000001f) {
             // vectors are linearly dependent or zero so no solution
             ALOGD_IF(DEBUG_STRATEGY, "  - no solution, norm=%f", norm);
-            return false;
+            return {};
         }
 
         float invNorm = 1.0f / norm;
@@ -549,6 +542,7 @@
     for (uint32_t h = 0; h < m; h++) {
         wy[h] = y[h] * w[h];
     }
+    std::array<float, VelocityTracker::MAX_DEGREE + 1> outB;
     for (uint32_t i = n; i != 0; ) {
         i--;
         outB[i] = vectorDot(&q[i][0], wy, m);
@@ -570,42 +564,46 @@
     }
     ymean /= m;
 
-    float sserr = 0;
-    float sstot = 0;
-    for (uint32_t h = 0; h < m; h++) {
-        float err = y[h] - outB[0];
-        float term = 1;
-        for (uint32_t i = 1; i < n; i++) {
-            term *= x[h];
-            err -= term * outB[i];
+    if (DEBUG_STRATEGY) {
+        float sserr = 0;
+        float sstot = 0;
+        for (uint32_t h = 0; h < m; h++) {
+            float err = y[h] - outB[0];
+            float term = 1;
+            for (uint32_t i = 1; i < n; i++) {
+                term *= x[h];
+                err -= term * outB[i];
+            }
+            sserr += w[h] * w[h] * err * err;
+            float var = y[h] - ymean;
+            sstot += w[h] * w[h] * var * var;
         }
-        sserr += w[h] * w[h] * err * err;
-        float var = y[h] - ymean;
-        sstot += w[h] * w[h] * var * var;
+        ALOGD("  - sserr=%f", sserr);
+        ALOGD("  - sstot=%f", sstot);
     }
-    *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "  - sserr=%f", sserr);
-    ALOGD_IF(DEBUG_STRATEGY, "  - sstot=%f", sstot);
-    ALOGD_IF(DEBUG_STRATEGY, "  - det=%f", *outDet);
-
-    return true;
+    return outB[1];
 }
 
 /*
  * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
  * the default implementation
  */
-static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
-        const std::vector<float>& x, const std::vector<float>& y) {
-    const size_t count = x.size();
-    LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
-    // Solving y = a*x^2 + b*x + c
+std::optional<float> LeastSquaresVelocityTrackerStrategy::solveUnweightedLeastSquaresDeg2(
+        const RingBuffer<Movement>& movements) const {
+    // Solving y = a*x^2 + b*x + c, where
+    //      - "x" is age (i.e. duration since latest movement) of the movemnets
+    //      - "y" is positions of the movements.
     float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
 
+    const size_t count = movements.size();
+    const Movement& newestMovement = movements[count - 1];
     for (size_t i = 0; i < count; i++) {
-        float xi = x[i];
-        float yi = y[i];
+        const Movement& movement = movements[i];
+        nsecs_t age = newestMovement.eventTime - movement.eventTime;
+        float xi = -age * SECONDS_PER_NANO;
+        float yi = movement.position;
+
         float xi2 = xi*xi;
         float xi3 = xi2*xi;
         float xi4 = xi3*xi;
@@ -632,124 +630,68 @@
         ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
         return std::nullopt;
     }
-    // Compute a
-    float numerator = Sx2y*Sxx - Sxy*Sxx2;
-    float a = numerator / denominator;
 
-    // Compute b
-    numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
-    float b = numerator / denominator;
-
-    // Compute c
-    float c = syi/count - b * sxi/count - a * sxi2/count;
-
-    return std::make_optional(std::array<float, 3>({c, b, a}));
+    return (Sxy * Sx2x2 - Sx2y * Sxx2) / denominator;
 }
 
-std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> LeastSquaresVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
+
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
+        return std::nullopt; // no data
+    }
+
+    uint32_t degree = mDegree;
+    if (degree > size - 1) {
+        degree = size - 1;
+    }
+
+    if (degree <= 0) {
+        return std::nullopt;
+    }
+
+    if (degree == 2 && mWeighting == Weighting::NONE) {
+        // Optimize unweighted, quadratic polynomial fit
+        return solveUnweightedLeastSquaresDeg2(movements);
+    }
+
     // Iterate over movement samples in reverse time order and collect samples.
     std::vector<float> positions;
     std::vector<float> w;
     std::vector<float> time;
 
-    uint32_t index = mIndex.at(pointerId);
-    const Movement& newestMovement = movementIt->second[index];
-    do {
-        const Movement& movement = movementIt->second[index];
-
+    const Movement& newestMovement = movements[size - 1];
+    for (ssize_t i = size - 1; i >= 0; i--) {
+        const Movement& movement = movements[i];
         nsecs_t age = newestMovement.eventTime - movement.eventTime;
-        if (age > HORIZON) {
-            break;
-        }
-        if (movement.eventTime == 0 && index != 0) {
-            // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's
-            // possible that not all entries are valid. We use a time=0 as a signal for those
-            // uninitialized values. If we encounter a time of 0 in a position
-            // that's > 0, it means that we hit the block where the data wasn't initialized.
-            // We still don't know whether the value at index=0, with eventTime=0 is valid.
-            // However, that's only possible when the value is by itself. So there's no hard in
-            // processing it anyways, since the velocity for a single point is zero, and this
-            // situation will only be encountered in artificial circumstances (in tests).
-            // In practice, time will never be 0.
-            break;
-        }
         positions.push_back(movement.position);
-        w.push_back(chooseWeight(pointerId, index));
+        w.push_back(chooseWeight(pointerId, i));
         time.push_back(-age * 0.000000001f);
-        index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (positions.size() < HISTORY_SIZE);
-
-    const size_t m = positions.size();
-    if (m == 0) {
-        return std::nullopt; // no data
     }
 
-    // Calculate a least squares polynomial fit.
-    uint32_t degree = mDegree;
-    if (degree > m - 1) {
-        degree = m - 1;
-    }
-
-    if (degree == 2 && mWeighting == Weighting::NONE) {
-        // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> coeff =
-                solveUnweightedLeastSquaresDeg2(time, positions);
-        if (coeff) {
-            VelocityTracker::Estimator estimator;
-            estimator.time = newestMovement.eventTime;
-            estimator.degree = 2;
-            estimator.confidence = 1;
-            for (size_t i = 0; i <= estimator.degree; i++) {
-                estimator.coeff[i] = (*coeff)[i];
-            }
-            return estimator;
-        }
-    } else if (degree >= 1) {
-        // General case for an Nth degree polynomial fit
-        float det;
-        uint32_t n = degree + 1;
-        VelocityTracker::Estimator estimator;
-        if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) {
-            estimator.time = newestMovement.eventTime;
-            estimator.degree = degree;
-            estimator.confidence = det;
-
-            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
-                     int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(),
-                     estimator.confidence);
-
-            return estimator;
-        }
-    }
-
-    // No velocity data available for this pointer, but we do have its current position.
-    VelocityTracker::Estimator estimator;
-    estimator.coeff[0] = positions[0];
-    estimator.time = newestMovement.eventTime;
-    estimator.degree = 0;
-    estimator.confidence = 1;
-    return estimator;
+    // General case for an Nth degree polynomial fit
+    return solveLeastSquares(time, positions, w, degree + 1);
 }
 
 float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const {
-    const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId);
+    const RingBuffer<Movement>& movements = mMovements.at(pointerId);
+    const size_t size = movements.size();
     switch (mWeighting) {
         case Weighting::DELTA: {
             // Weight points based on how much time elapsed between them and the next
             // point so that points that "cover" a shorter time span are weighed less.
             //   delta  0ms: 0.5
             //   delta 10ms: 1.0
-            if (index == mIndex.at(pointerId)) {
+            if (index == size - 1) {
                 return 1.0f;
             }
-            uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
             float deltaMillis =
-                    (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f;
+                    (movements[index + 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (deltaMillis < 0) {
                 return 0.5f;
             }
@@ -766,8 +708,7 @@
             //   age 50ms: 1.0
             //   age 60ms: 0.5
             float ageMillis =
-                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
-                    0.000001f;
+                    (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (ageMillis < 0) {
                 return 0.5f;
             }
@@ -789,8 +730,7 @@
             //   age  50ms: 1.0
             //   age 100ms: 0.5
             float ageMillis =
-                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
-                    0.000001f;
+                    (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
             if (ageMillis < 50) {
                 return 1.0f;
             }
@@ -830,13 +770,9 @@
     mPointerIdBits.markBit(pointerId);
 }
 
-std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> IntegratingVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     if (mPointerIdBits.hasBit(pointerId)) {
-        const State& state = mPointerState[pointerId];
-        VelocityTracker::Estimator estimator;
-        populateEstimator(state, &estimator);
-        return estimator;
+        return mPointerState[pointerId].vel;
     }
 
     return std::nullopt;
@@ -886,77 +822,39 @@
     state.pos = pos;
 }
 
-void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->time = state.updateTime;
-    outEstimator->confidence = 1.0f;
-    outEstimator->degree = state.degree;
-    outEstimator->coeff[0] = state.pos;
-    outEstimator->coeff[1] = state.vel;
-    outEstimator->coeff[2] = state.accel / 2;
-}
-
-
 // --- LegacyVelocityTrackerStrategy ---
 
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {}
+LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy()
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            false /*maintainHorizonDuringAdd*/) {}
 
 LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
 }
 
-void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
-
-std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> LegacyVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
-    const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)];
+
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
+        return std::nullopt; // no data
+    }
+
+    const Movement& newestMovement = movements[size - 1];
 
     // Find the oldest sample that contains the pointer and that is not older than HORIZON.
     nsecs_t minTime = newestMovement.eventTime - HORIZON;
-    uint32_t oldestIndex = mIndex.at(pointerId);
-    uint32_t numTouches = 1;
-    do {
-        uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
-        const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex];
+    uint32_t oldestIndex = size - 1;
+    for (ssize_t i = size - 1; i >= 0; i--) {
+        const Movement& nextOldestMovement = movements[i];
         if (nextOldestMovement.eventTime < minTime) {
             break;
         }
-        oldestIndex = nextOldestIndex;
-    } while (++numTouches < HISTORY_SIZE);
+        oldestIndex = i;
+    }
 
     // Calculate an exponentially weighted moving average of the velocity estimate
     // at different points in time measured relative to the oldest sample.
@@ -970,17 +868,13 @@
     // 16ms apart but some consecutive samples could be only 0.5sm apart because
     // the hardware or driver reports them irregularly or in bursts.
     float accumV = 0;
-    uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
-    const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex];
+    const Movement& oldestMovement = movements[oldestIndex];
     float oldestPosition = oldestMovement.position;
     nsecs_t lastDuration = 0;
 
-    while (numTouches-- > 1) {
-        if (++index == HISTORY_SIZE) {
-            index = 0;
-        }
-        const Movement& movement = mMovements.at(pointerId)[index];
+    for (size_t i = oldestIndex; i < size; i++) {
+        const Movement& movement = movements[i];
         nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
 
         // If the duration between samples is small, we may significantly overestimate
@@ -996,62 +890,22 @@
         }
     }
 
-    // Report velocity.
-    float newestPosition = newestMovement.position;
-    VelocityTracker::Estimator estimator;
-    estimator.time = newestMovement.eventTime;
-    estimator.confidence = 1;
-    estimator.coeff[0] = newestPosition;
     if (samplesUsed) {
-        estimator.coeff[1] = accumV;
-        estimator.degree = 1;
-    } else {
-        estimator.degree = 0;
+        return accumV;
     }
-    return estimator;
+    return std::nullopt;
 }
 
 // --- ImpulseVelocityTrackerStrategy ---
 
 ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
-      : mDeltaValues(deltaValues) {}
+      : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+                                            true /*maintainHorizonDuringAdd*/),
+        mDeltaValues(deltaValues) {}
 
 ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
 }
 
-void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
-    mIndex.erase(pointerId);
-    mMovements.erase(pointerId);
-}
-
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
-                                                 float position) {
-    // If data for this pointer already exists, we have a valid entry at the position of
-    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
-    // to the next position in the circular buffer and write the new Movement there. Otherwise,
-    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
-    // for this pointer and write to the first position.
-    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
-    auto [indexIt, _] = mIndex.insert({pointerId, 0});
-    size_t& index = indexIt->second;
-    if (!inserted && movementIt->second[index].eventTime != eventTime) {
-        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
-        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
-        // the new pointer. If the eventtimes for both events are identical, just update the data
-        // for this time.
-        // We only compare against the last value, as it is likely that addMovement is called
-        // in chronological order as events occur.
-        index++;
-    }
-    if (index == HISTORY_SIZE) {
-        index = 0;
-    }
-
-    Movement& movement = movementIt->second[index];
-    movement.eventTime = eventTime;
-    movement.position = position;
-}
-
 /**
  * Calculate the total impulse provided to the screen and the resulting velocity.
  *
@@ -1126,112 +980,44 @@
     return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
 }
 
-static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count,
-                                      bool deltaValues) {
-    // The input should be in reversed time order (most recent sample at index i=0)
-    // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
-    static constexpr float SECONDS_PER_NANO = 1E-9;
-
-    if (count < 2) {
-        return 0; // if 0 or 1 points, velocity is zero
-    }
-    if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
-        ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
-    }
-
-    // If the data values are delta values, we do not have to calculate deltas here.
-    // We can use the delta values directly, along with the calculated time deltas.
-    // Since the data value input is in reversed time order:
-    //      [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1])
-    //      [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1])
-    // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4].
-    // Since the input is in reversed time order, the input values for this function would be
-    // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively.
-    //
-    // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1}
-    // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4}
-
-    if (count == 2) { // if 2 points, basic linear calculation
-        if (t[1] == t[0]) {
-            ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
-            return 0;
-        }
-        const float deltaX = deltaValues ? -x[0] : x[1] - x[0];
-        return deltaX / (SECONDS_PER_NANO * (t[1] - t[0]));
-    }
-    // Guaranteed to have at least 3 points here
-    float work = 0;
-    for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time
-        if (t[i] == t[i-1]) {
-            ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]);
-            continue;
-        }
-        float vprev = kineticEnergyToVelocity(work); // v[i-1]
-        const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1];
-        float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
-        work += (vcurr - vprev) * fabsf(vcurr);
-        if (i == count - 1) {
-            work *= 0.5; // initial condition, case 2) above
-        }
-    }
-    return kineticEnergyToVelocity(work);
-}
-
-std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator(
-        int32_t pointerId) const {
+std::optional<float> ImpulseVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
     const auto movementIt = mMovements.find(pointerId);
     if (movementIt == mMovements.end()) {
         return std::nullopt; // no data
     }
 
-    // Iterate over movement samples in reverse time order and collect samples.
-    float positions[HISTORY_SIZE];
-    nsecs_t time[HISTORY_SIZE];
-    size_t m = 0; // number of points that will be used for fitting
-    size_t index = mIndex.at(pointerId);
-    const Movement& newestMovement = movementIt->second[index];
-    do {
-        const Movement& movement = movementIt->second[index];
-
-        nsecs_t age = newestMovement.eventTime - movement.eventTime;
-        if (age > HORIZON) {
-            break;
-        }
-        if (movement.eventTime == 0 && index != 0) {
-            // All eventTime's are initialized to 0. If we encounter a time of 0 in a position
-            // that's >0, it means that we hit the block where the data wasn't initialized.
-            // It's also possible that the sample at 0 would be invalid, but there's no harm in
-            // processing it, since it would be just a single point, and will only be encountered
-            // in artificial circumstances (in tests).
-            break;
-        }
-
-        positions[m] = movement.position;
-        time[m] = movement.eventTime;
-        index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (++m < HISTORY_SIZE);
-
-    if (m == 0) {
+    const RingBuffer<Movement>& movements = movementIt->second;
+    const size_t size = movements.size();
+    if (size == 0) {
         return std::nullopt; // no data
     }
-    VelocityTracker::Estimator estimator;
-    estimator.coeff[0] = 0;
-    estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
-    estimator.coeff[2] = 0;
 
-    estimator.time = newestMovement.eventTime;
-    estimator.degree = 2; // similar results to 2nd degree fit
-    estimator.confidence = 1;
+    float work = 0;
+    for (size_t i = 0; i < size - 1; i++) {
+        const Movement& mvt = movements[i];
+        const Movement& nextMvt = movements[i + 1];
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]);
+        float vprev = kineticEnergyToVelocity(work);
+        float delta = mDeltaValues ? nextMvt.position : nextMvt.position - mvt.position;
+        float vcurr = delta / (SECONDS_PER_NANO * (nextMvt.eventTime - mvt.eventTime));
+        work += (vcurr - vprev) * fabsf(vcurr);
+
+        if (i == 0) {
+            work *= 0.5; // initial condition, case 2) above
+        }
+    }
+
+    const float velocity = kineticEnergyToVelocity(work);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", velocity);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
         // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
         // X axis chosen arbitrarily for velocity comparisons.
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
-        for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]);
+        for (size_t i = 0; i < size; i++) {
+            const Movement& mvt = movements[i];
+            lsq2.addMovement(mvt.eventTime, pointerId, AMOTION_EVENT_AXIS_X, mvt.position);
         }
         std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
         if (v) {
@@ -1240,7 +1026,7 @@
             ALOGD("lsq2 velocity: could not compute velocity");
         }
     }
-    return estimator;
+    return velocity;
 }
 
 } // namespace android
diff --git a/libs/input/input_verifier.rs b/libs/input/input_verifier.rs
deleted file mode 100644
index 2e05a63..0000000
--- a/libs/input/input_verifier.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright 2023 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.
- */
-
-//! Validate the incoming motion stream.
-//! This class is not thread-safe.
-//! State is stored in the "InputVerifier" object
-//! that can be created via the 'create' method.
-//! Usage:
-//! Box<InputVerifier> verifier = create("inputChannel name");
-//! result = process_movement(verifier, ...);
-//! if (result) {
-//!    crash(result.error_message());
-//! }
-
-use std::collections::HashMap;
-use std::collections::HashSet;
-
-use bitflags::bitflags;
-use log::info;
-
-#[cxx::bridge(namespace = "android::input")]
-mod ffi {
-    #[namespace = "android"]
-    unsafe extern "C++" {
-        include!("ffi/FromRustToCpp.h");
-        fn shouldLog(tag: &str) -> bool;
-    }
-    #[namespace = "android::input::verifier"]
-    extern "Rust" {
-        type InputVerifier;
-
-        fn create(name: String) -> Box<InputVerifier>;
-        fn process_movement(
-            verifier: &mut InputVerifier,
-            device_id: i32,
-            action: u32,
-            pointer_properties: &[RustPointerProperties],
-            flags: i32,
-        ) -> String;
-    }
-
-    pub struct RustPointerProperties {
-        id: i32,
-    }
-}
-
-use crate::ffi::shouldLog;
-use crate::ffi::RustPointerProperties;
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-struct DeviceId(i32);
-
-fn process_movement(
-    verifier: &mut InputVerifier,
-    device_id: i32,
-    action: u32,
-    pointer_properties: &[RustPointerProperties],
-    flags: i32,
-) -> String {
-    let result = verifier.process_movement(
-        DeviceId(device_id),
-        action,
-        pointer_properties,
-        Flags::from_bits(flags).unwrap(),
-    );
-    match result {
-        Ok(()) => "".to_string(),
-        Err(e) => e,
-    }
-}
-
-fn create(name: String) -> Box<InputVerifier> {
-    Box::new(InputVerifier::new(&name))
-}
-
-#[repr(u32)]
-enum MotionAction {
-    Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-    Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
-    Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-    Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
-    Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
-    PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
-    PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
-    HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-    HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
-    HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
-    Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
-    ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-    ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-}
-
-fn get_action_index(action: u32) -> usize {
-    let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
-        >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-    index.try_into().unwrap()
-}
-
-impl From<u32> for MotionAction {
-    fn from(action: u32) -> Self {
-        let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
-        let action_index = get_action_index(action);
-        match action_masked {
-            input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
-            input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
-            input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
-            input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
-            input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
-            input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
-                MotionAction::PointerDown { action_index }
-            }
-            input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
-                MotionAction::PointerUp { action_index }
-            }
-            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
-            input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
-            input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
-            input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
-            _ => panic!("Unknown action: {}", action),
-        }
-    }
-}
-
-bitflags! {
-    struct Flags: i32 {
-        const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
-    }
-}
-
-fn motion_action_to_string(action: u32) -> String {
-    match action.into() {
-        MotionAction::Down => "DOWN".to_string(),
-        MotionAction::Up => "UP".to_string(),
-        MotionAction::Move => "MOVE".to_string(),
-        MotionAction::Cancel => "CANCEL".to_string(),
-        MotionAction::Outside => "OUTSIDE".to_string(),
-        MotionAction::PointerDown { action_index } => {
-            format!("POINTER_DOWN({})", action_index)
-        }
-        MotionAction::PointerUp { action_index } => {
-            format!("POINTER_UP({})", action_index)
-        }
-        MotionAction::HoverMove => "HOVER_MOVE".to_string(),
-        MotionAction::Scroll => "SCROLL".to_string(),
-        MotionAction::HoverEnter => "HOVER_ENTER".to_string(),
-        MotionAction::HoverExit => "HOVER_EXIT".to_string(),
-        MotionAction::ButtonPress => "BUTTON_PRESS".to_string(),
-        MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(),
-    }
-}
-
-/**
- * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
- * to inconsistent events.
- * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
- */
-fn log_events() -> bool {
-    shouldLog("InputVerifierLogEvents")
-}
-
-struct InputVerifier {
-    name: String,
-    touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
-}
-
-impl InputVerifier {
-    fn new(name: &str) -> Self {
-        logger::init(
-            logger::Config::default()
-                .with_tag_on_device("InputVerifier")
-                .with_min_level(log::Level::Trace),
-        );
-        Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() }
-    }
-
-    fn process_movement(
-        &mut self,
-        device_id: DeviceId,
-        action: u32,
-        pointer_properties: &[RustPointerProperties],
-        flags: Flags,
-    ) -> Result<(), String> {
-        if log_events() {
-            info!(
-                "Processing {} for device {:?} ({} pointer{}) on {}",
-                motion_action_to_string(action),
-                device_id,
-                pointer_properties.len(),
-                if pointer_properties.len() == 1 { "" } else { "s" },
-                self.name
-            );
-        }
-
-        match action.into() {
-            MotionAction::Down => {
-                let it = self
-                    .touching_pointer_ids_by_device
-                    .entry(device_id)
-                    .or_insert_with(HashSet::new);
-                let pointer_id = pointer_properties[0].id;
-                if it.contains(&pointer_id) {
-                    return Err(format!(
-                        "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
-                        self.name, device_id, it
-                    ));
-                }
-                it.insert(pointer_id);
-            }
-            MotionAction::PointerDown { action_index } => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
-                    return Err(format!(
-                        "{}: Received POINTER_DOWN but no pointers are currently down \
-                        for device {:?}",
-                        self.name, device_id
-                    ));
-                }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
-                let pointer_id = pointer_properties[action_index].id;
-                if it.contains(&pointer_id) {
-                    return Err(format!(
-                        "{}: Pointer with id={} not found in the properties",
-                        self.name, pointer_id
-                    ));
-                }
-                it.insert(pointer_id);
-            }
-            MotionAction::Move => {
-                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
-                    return Err(format!(
-                        "{}: ACTION_MOVE touching pointers don't match",
-                        self.name
-                    ));
-                }
-            }
-            MotionAction::PointerUp { action_index } => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
-                    return Err(format!(
-                        "{}: Received POINTER_UP but no pointers are currently down for device \
-                        {:?}",
-                        self.name, device_id
-                    ));
-                }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
-                let pointer_id = pointer_properties[action_index].id;
-                it.remove(&pointer_id);
-            }
-            MotionAction::Up => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
-                    return Err(format!(
-                        "{} Received ACTION_UP but no pointers are currently down for device {:?}",
-                        self.name, device_id
-                    ));
-                }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
-                if it.len() != 1 {
-                    return Err(format!(
-                        "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
-                        self.name, it, device_id
-                    ));
-                }
-                let pointer_id = pointer_properties[0].id;
-                if !it.contains(&pointer_id) {
-                    return Err(format!(
-                        "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
-                        {:?} for device {:?}",
-                        self.name, pointer_id, it, device_id
-                    ));
-                }
-                it.clear();
-            }
-            MotionAction::Cancel => {
-                if flags.contains(Flags::CANCELED) {
-                    return Err(format!(
-                        "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
-                        self.name
-                    ));
-                }
-                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
-                    return Err(format!(
-                        "{}: Got ACTION_CANCEL, but the pointers don't match. \
-                        Existing pointers: {:?}",
-                        self.name, self.touching_pointer_ids_by_device
-                    ));
-                }
-                self.touching_pointer_ids_by_device.remove(&device_id);
-            }
-            _ => return Ok(()),
-        }
-        Ok(())
-    }
-
-    fn ensure_touching_pointers_match(
-        &self,
-        device_id: DeviceId,
-        pointer_properties: &[RustPointerProperties],
-    ) -> bool {
-        let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
-            return false;
-        };
-
-        for pointer_property in pointer_properties.iter() {
-            let pointer_id = pointer_property.id;
-            if !pointers.contains(&pointer_id) {
-                return false;
-            }
-        }
-        true
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::DeviceId;
-    use crate::Flags;
-    use crate::InputVerifier;
-    use crate::RustPointerProperties;
-    #[test]
-    fn single_pointer_stream() {
-        let mut verifier = InputVerifier::new("Test");
-        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-    }
-
-    #[test]
-    fn multi_device_stream() {
-        let mut verifier = InputVerifier::new("Test");
-        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(2),
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(2),
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_ok());
-    }
-
-    #[test]
-    fn test_invalid_up() {
-        let mut verifier = InputVerifier::new("Test");
-        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
-        assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                &pointer_properties,
-                Flags::empty(),
-            )
-            .is_err());
-    }
-}
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
new file mode 100644
index 0000000..018d199
--- /dev/null
+++ b/libs/input/rust/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2023 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.
+
+rust_defaults {
+    name: "libinput_rust_defaults",
+    crate_name: "input",
+    srcs: ["lib.rs"],
+    host_supported: true,
+    rustlibs: [
+        "libbitflags",
+        "libcxx",
+        "libinput_bindgen",
+        "liblogger",
+        "liblog_rust",
+        "inputconstants-rust",
+    ],
+    whole_static_libs: [
+        "libinput_from_rust_to_cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+rust_library {
+    name: "libinput_rust",
+    defaults: ["libinput_rust_defaults"],
+}
+
+rust_ffi_static {
+    name: "libinput_rust_ffi",
+    defaults: ["libinput_rust_defaults"],
+}
+
+rust_test {
+    name: "libinput_rust_test",
+    defaults: ["libinput_rust_defaults"],
+    test_options: {
+        unit_test: true,
+    },
+    test_suites: ["device_tests"],
+    sanitize: {
+        hwaddress: true,
+    },
+}
+
+genrule {
+    name: "libinput_cxx_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) >> $(out)",
+    srcs: ["lib.rs"],
+    out: ["input_cxx_bridge_generated.cpp"],
+}
+
+genrule {
+    name: "libinput_cxx_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+    srcs: ["lib.rs"],
+    out: ["input_cxx_bridge.rs.h"],
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
new file mode 100644
index 0000000..a308c26
--- /dev/null
+++ b/libs/input/rust/input.rs
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2023 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.
+ */
+
+//! Common definitions of the Android Input Framework in rust.
+
+use bitflags::bitflags;
+use std::fmt;
+
+/// The InputDevice ID.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct DeviceId(pub i32);
+
+/// A rust enum representation of a MotionEvent action.
+#[repr(u32)]
+pub enum MotionAction {
+    /// ACTION_DOWN
+    Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+    /// ACTION_UP
+    Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
+    /// ACTION_MOVE
+    Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+    /// ACTION_CANCEL
+    Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+    /// ACTION_OUTSIDE
+    Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
+    /// ACTION_POINTER_DOWN
+    PointerDown {
+        /// The index of the affected pointer.
+        action_index: usize,
+    } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
+    /// ACTION_POINTER_UP
+    PointerUp {
+        /// The index of the affected pointer.
+        action_index: usize,
+    } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
+    /// ACTION_HOVER_ENTER
+    HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+    /// ACTION_HOVER_MOVE
+    HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+    /// ACTION_HOVER_EXIT
+    HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+    /// ACTION_SCROLL
+    Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
+    /// ACTION_BUTTON_PRESS
+    ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+    /// ACTION_BUTTON_RELEASE
+    ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+}
+
+impl fmt::Display for MotionAction {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            MotionAction::Down => write!(f, "DOWN"),
+            MotionAction::Up => write!(f, "UP"),
+            MotionAction::Move => write!(f, "MOVE"),
+            MotionAction::Cancel => write!(f, "CANCEL"),
+            MotionAction::Outside => write!(f, "OUTSIDE"),
+            MotionAction::PointerDown { action_index } => {
+                write!(f, "POINTER_DOWN({})", action_index)
+            }
+            MotionAction::PointerUp { action_index } => write!(f, "POINTER_UP({})", action_index),
+            MotionAction::HoverMove => write!(f, "HOVER_MOVE"),
+            MotionAction::Scroll => write!(f, "SCROLL"),
+            MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
+            MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
+            MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
+            MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+        }
+    }
+}
+
+impl From<u32> for MotionAction {
+    fn from(action: u32) -> Self {
+        let (action_masked, action_index) = MotionAction::breakdown_action(action);
+        match action_masked {
+            input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
+            input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
+            input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
+            input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
+            input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
+            input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
+                MotionAction::PointerDown { action_index }
+            }
+            input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
+                MotionAction::PointerUp { action_index }
+            }
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
+            input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+            _ => panic!("Unknown action: {}", action),
+        }
+    }
+}
+
+impl MotionAction {
+    fn breakdown_action(action: u32) -> (u32, usize) {
+        let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
+        let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+            >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+        (action_masked, index.try_into().unwrap())
+    }
+}
+
+bitflags! {
+    /// MotionEvent flags.
+    pub struct MotionFlags: i32 {
+        /// FLAG_CANCELED
+        const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
+    }
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
new file mode 100644
index 0000000..64c0466
--- /dev/null
+++ b/libs/input/rust/input_verifier.rs
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Contains the InputVerifier, used to validate a stream of input events.
+
+use crate::ffi::RustPointerProperties;
+use crate::input::{DeviceId, MotionAction, MotionFlags};
+use log::info;
+use std::collections::HashMap;
+use std::collections::HashSet;
+
+/// The InputVerifier is used to validate a stream of input events.
+pub struct InputVerifier {
+    name: String,
+    should_log: bool,
+    touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+    hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+}
+
+impl InputVerifier {
+    /// Create a new InputVerifier.
+    pub fn new(name: &str, should_log: bool) -> Self {
+        logger::init(
+            logger::Config::default()
+                .with_tag_on_device("InputVerifier")
+                .with_min_level(log::Level::Trace),
+        );
+        Self {
+            name: name.to_owned(),
+            should_log,
+            touching_pointer_ids_by_device: HashMap::new(),
+            hovering_pointer_ids_by_device: HashMap::new(),
+        }
+    }
+
+    /// Process a pointer movement event from an InputDevice.
+    /// If the event is not valid, we return an error string that describes the issue.
+    pub fn process_movement(
+        &mut self,
+        device_id: DeviceId,
+        action: u32,
+        pointer_properties: &[RustPointerProperties],
+        flags: MotionFlags,
+    ) -> Result<(), String> {
+        if self.should_log {
+            info!(
+                "Processing {} for device {:?} ({} pointer{}) on {}",
+                MotionAction::from(action).to_string(),
+                device_id,
+                pointer_properties.len(),
+                if pointer_properties.len() == 1 { "" } else { "s" },
+                self.name
+            );
+        }
+
+        match action.into() {
+            MotionAction::Down => {
+                let it = self
+                    .touching_pointer_ids_by_device
+                    .entry(device_id)
+                    .or_insert_with(HashSet::new);
+                let pointer_id = pointer_properties[0].id;
+                if it.contains(&pointer_id) {
+                    return Err(format!(
+                        "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+                        self.name, device_id, it
+                    ));
+                }
+                it.insert(pointer_id);
+            }
+            MotionAction::PointerDown { action_index } => {
+                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Received POINTER_DOWN but no pointers are currently down \
+                        for device {:?}",
+                        self.name, device_id
+                    ));
+                }
+                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                let pointer_id = pointer_properties[action_index].id;
+                if it.contains(&pointer_id) {
+                    return Err(format!(
+                        "{}: Pointer with id={} not found in the properties",
+                        self.name, pointer_id
+                    ));
+                }
+                it.insert(pointer_id);
+            }
+            MotionAction::Move => {
+                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+                    return Err(format!(
+                        "{}: ACTION_MOVE touching pointers don't match",
+                        self.name
+                    ));
+                }
+            }
+            MotionAction::PointerUp { action_index } => {
+                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Received POINTER_UP but no pointers are currently down for device \
+                        {:?}",
+                        self.name, device_id
+                    ));
+                }
+                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                let pointer_id = pointer_properties[action_index].id;
+                it.remove(&pointer_id);
+            }
+            MotionAction::Up => {
+                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{} Received ACTION_UP but no pointers are currently down for device {:?}",
+                        self.name, device_id
+                    ));
+                }
+                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                if it.len() != 1 {
+                    return Err(format!(
+                        "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
+                        self.name, it, device_id
+                    ));
+                }
+                let pointer_id = pointer_properties[0].id;
+                if !it.contains(&pointer_id) {
+                    return Err(format!(
+                        "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
+                        {:?} for device {:?}",
+                        self.name, pointer_id, it, device_id
+                    ));
+                }
+                self.touching_pointer_ids_by_device.remove(&device_id);
+            }
+            MotionAction::Cancel => {
+                if !flags.contains(MotionFlags::CANCELED) {
+                    return Err(format!(
+                        "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
+                        self.name
+                    ));
+                }
+                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+                    return Err(format!(
+                        "{}: Got ACTION_CANCEL, but the pointers don't match. \
+                        Existing pointers: {:?}",
+                        self.name, self.touching_pointer_ids_by_device
+                    ));
+                }
+                self.touching_pointer_ids_by_device.remove(&device_id);
+            }
+            /*
+             * The hovering protocol currently supports a single pointer only, because we do not
+             * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
+             * Still, we are keeping the infrastructure here pretty general in case that is
+             * eventually supported.
+             */
+            MotionAction::HoverEnter => {
+                if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
+                        {:?}",
+                        self.name, device_id, self.hovering_pointer_ids_by_device
+                    ));
+                }
+                let it = self
+                    .hovering_pointer_ids_by_device
+                    .entry(device_id)
+                    .or_insert_with(HashSet::new);
+                it.insert(pointer_properties[0].id);
+            }
+            MotionAction::HoverMove => {
+                // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
+                // If there was no prior HOVER_ENTER, just start a new hovering pointer.
+                let it = self
+                    .hovering_pointer_ids_by_device
+                    .entry(device_id)
+                    .or_insert_with(HashSet::new);
+                it.insert(pointer_properties[0].id);
+            }
+            MotionAction::HoverExit => {
+                if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
+                        self.name, device_id
+                    ));
+                }
+                let pointer_id = pointer_properties[0].id;
+                let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                it.remove(&pointer_id);
+
+                if !it.is_empty() {
+                    return Err(format!(
+                        "{}: Removed hovering pointer {}, but pointers are still\
+                               hovering for device {:?}: {:?}",
+                        self.name, pointer_id, device_id, it
+                    ));
+                }
+                self.hovering_pointer_ids_by_device.remove(&device_id);
+            }
+            _ => return Ok(()),
+        }
+        Ok(())
+    }
+
+    /// Notify the verifier that the device has been reset, which will cause the verifier to erase
+    /// the current internal state for this device. Subsequent events from this device are expected
+    //// to start a new gesture.
+    pub fn reset_device(&mut self, device_id: DeviceId) {
+        self.touching_pointer_ids_by_device.remove(&device_id);
+        self.hovering_pointer_ids_by_device.remove(&device_id);
+    }
+
+    fn ensure_touching_pointers_match(
+        &self,
+        device_id: DeviceId,
+        pointer_properties: &[RustPointerProperties],
+    ) -> bool {
+        let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
+            return false;
+        };
+
+        for pointer_property in pointer_properties.iter() {
+            let pointer_id = pointer_property.id;
+            if !pointers.contains(&pointer_id) {
+                return false;
+            }
+        }
+        true
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::input_verifier::InputVerifier;
+    use crate::DeviceId;
+    use crate::MotionFlags;
+    use crate::RustPointerProperties;
+    #[test]
+    fn single_pointer_stream() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_UP,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
+
+    #[test]
+    fn multi_device_stream() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(2),
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(2),
+                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_UP,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
+
+    #[test]
+    fn action_cancel() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+                &pointer_properties,
+                MotionFlags::CANCELED,
+            )
+            .is_ok());
+    }
+
+    #[test]
+    fn invalid_action_cancel() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+                &pointer_properties,
+                MotionFlags::empty(), // forgot to set FLAG_CANCELED
+            )
+            .is_err());
+    }
+
+    #[test]
+    fn invalid_up() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_UP,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_err());
+    }
+
+    #[test]
+    fn correct_hover_sequence() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
+
+    #[test]
+    fn double_hover_enter() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_err());
+    }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
new file mode 100644
index 0000000..892f558
--- /dev/null
+++ b/libs/input/rust/lib.rs
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 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.
+ */
+
+//! The rust component of libinput.
+
+mod input;
+mod input_verifier;
+
+pub use input::{DeviceId, MotionAction, MotionFlags};
+pub use input_verifier::InputVerifier;
+
+#[cxx::bridge(namespace = "android::input")]
+mod ffi {
+    #[namespace = "android"]
+    unsafe extern "C++" {
+        include!("ffi/FromRustToCpp.h");
+        fn shouldLog(tag: &str) -> bool;
+    }
+
+    #[namespace = "android::input::verifier"]
+    extern "Rust" {
+        /// Used to validate the incoming motion stream.
+        /// This class is not thread-safe.
+        /// State is stored in the "InputVerifier" object
+        /// that can be created via the 'create' method.
+        /// Usage:
+        ///
+        /// ```ignore
+        /// Box<InputVerifier> verifier = create("inputChannel name");
+        /// result = process_movement(verifier, ...);
+        /// if (result) {
+        ///    crash(result.error_message());
+        /// }
+        /// ```
+        type InputVerifier;
+        fn create(name: String) -> Box<InputVerifier>;
+        fn process_movement(
+            verifier: &mut InputVerifier,
+            device_id: i32,
+            action: u32,
+            pointer_properties: &[RustPointerProperties],
+            flags: i32,
+        ) -> String;
+        fn reset_device(verifier: &mut InputVerifier, device_id: i32);
+    }
+
+    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+    pub struct RustPointerProperties {
+        pub id: i32,
+    }
+}
+
+use crate::ffi::RustPointerProperties;
+
+fn create(name: String) -> Box<InputVerifier> {
+    Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
+}
+
+fn process_movement(
+    verifier: &mut InputVerifier,
+    device_id: i32,
+    action: u32,
+    pointer_properties: &[RustPointerProperties],
+    flags: i32,
+) -> String {
+    let result = verifier.process_movement(
+        DeviceId(device_id),
+        action,
+        pointer_properties,
+        MotionFlags::from_bits(flags).unwrap(),
+    );
+    match result {
+        Ok(()) => "".to_string(),
+        Err(e) => e,
+    }
+}
+
+fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
+    verifier.reset_device(DeviceId(device_id));
+}
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ae72109..ffebff1 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -42,8 +42,8 @@
 // here EV = expected value, tol = VELOCITY_TOLERANCE
 constexpr float VELOCITY_TOLERANCE = 0.2;
 
-// estimate coefficients must be within 0.001% of the target value
-constexpr float COEFFICIENT_TOLERANCE = 0.00001;
+// quadratic velocity must be within 0.001% of the target value
+constexpr float QUADRATIC_VELOCITY_TOLERANCE = 0.00001;
 
 // --- VelocityTrackerTest ---
 class VelocityTrackerTest : public testing::Test { };
@@ -76,10 +76,6 @@
     }
 }
 
-static void checkCoefficient(float actual, float target) {
-    EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
-}
-
 struct Position {
     float x;
     float y;
@@ -284,21 +280,20 @@
     checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
 }
 
-static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
-                                             const std::array<float, 3>& coefficients) {
+static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
+                                             float velocity) {
     VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
     std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0);
-    std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0);
-    EXPECT_TRUE(estimatorX);
-    EXPECT_TRUE(estimatorY);
-    for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient((*estimatorX).coeff[i], coefficients[i]);
-        checkCoefficient((*estimatorY).coeff[i], coefficients[i]);
-    }
+    std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0);
+    std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+    ASSERT_TRUE(velocityX);
+    ASSERT_TRUE(velocityY);
+
+    EXPECT_NEAR_BY_FRACTION(*velocityX, velocity, QUADRATIC_VELOCITY_TOLERANCE);
+    EXPECT_NEAR_BY_FRACTION(*velocityY, velocity, QUADRATIC_VELOCITY_TOLERANCE);
 }
 
 /*
@@ -461,8 +456,6 @@
 
     EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
 
-    EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
-
     VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000);
     for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
         EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id));
@@ -1074,7 +1067,7 @@
  * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be
  * part of the fitted data), this can cause large velocity values to be reported instead.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_ThreeFingerTap) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0us,      {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
         { 10800us,  {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN
@@ -1162,7 +1155,7 @@
  * ================== Tests for least squares fitting ==============================================
  *
  * Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy
- * getEstimator function. In particular:
+ * getVelocity function. In particular:
  * - inside the function, time gets converted from nanoseconds to seconds
  *   before being used in the fit.
  * - any values that are older than 100 ms are being discarded.
@@ -1183,7 +1176,7 @@
  * The coefficients are (0, 0, 1).
  * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Constant) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} }, // 0 s
         { 1ms, {{1, 1}} }, // 0.001 s
@@ -1195,13 +1188,13 @@
     // -0.002, 1
     // -0.001, 1
     // -0.ms, 1
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({1, 0, 0}));
+    computeAndCheckQuadraticVelocity(motions, 0);
 }
 
 /*
  * Straight line y = x :: the constant and quadratic coefficients are zero.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Linear) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{-2, -2}} },
         { 1ms, {{-1, -1}} },
@@ -1213,13 +1206,13 @@
     // -0.002, -2
     // -0.001, -1
     // -0.000,  0
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 1E3, 0}));
+    computeAndCheckQuadraticVelocity(motions, 1E3);
 }
 
 /*
  * Parabola
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
@@ -1231,13 +1224,13 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 8
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+    computeAndCheckQuadraticVelocity(motions, 4.5E3);
 }
 
 /*
  * Parabola
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic2) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
@@ -1249,13 +1242,13 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 9
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({9, 6E3, 1E6}));
+    computeAndCheckQuadraticVelocity(motions, 6E3);
 }
 
 /*
  * Parabola :: y = x^2 :: the constant and linear coefficients are zero.
  */
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic3) {
     std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{4, 4}} },
         { 1ms, {{1, 1}} },
@@ -1267,7 +1260,7 @@
     // -0.002, 4
     // -0.001, 1
     // -0.000, 0
-    computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6}));
+    computeAndCheckQuadraticVelocity(motions, 0E3);
 }
 
 // Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity.
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 23c99b0..dad3c19 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,6 +20,11 @@
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <SkImage.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+
+#include <android/hardware_buffer.h>
 #include "ColorSpaces.h"
 #include "log/log_main.h"
 #include "utils/Trace.h"
@@ -79,7 +84,7 @@
 
 // releaseImageProc is invoked by SkImage, when the texture is no longer in use.
 // "releaseContext" contains an "AutoBackendTexture*".
-void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) {
+void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContext) {
     AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
     textureRelease->unref(false);
 }
@@ -136,8 +141,9 @@
     }
 
     sk_sp<SkImage> image =
-            SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType,
-                                     alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
+            SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin,
+                                        colorType, alphaType, toSkColorSpace(dataspace),
+                                        releaseImageProc, this);
     if (image.get()) {
         // The following ref will be counteracted by releaseProc, when SkImage is discarded.
         ref();
@@ -157,10 +163,10 @@
     LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
     if (!mSurface.get() || mDataspace != dataspace) {
         sk_sp<SkSurface> surface =
-                SkSurface::MakeFromBackendTexture(context, mBackendTexture,
-                                                  kTopLeft_GrSurfaceOrigin, 0, mColorType,
-                                                  toSkColorSpace(dataspace), nullptr,
-                                                  releaseSurfaceProc, this);
+                SkSurfaces::WrapBackendTexture(context, mBackendTexture,
+                                               kTopLeft_GrSurfaceOrigin, 0, mColorType,
+                                               toSkColorSpace(dataspace), nullptr,
+                                               releaseSurfaceProc, this);
         if (surface.get()) {
             // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
             ref();
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 00b901b..509ac40 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -144,7 +144,7 @@
     CleanupManager& mCleanupMgr;
 
     static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
-    static void releaseImageProc(SkImage::ReleaseContext releaseContext);
+    static void releaseImageProc(SkImages::ReleaseContext releaseContext);
 
     int mUsageCount = 0;
 
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 18d9864..871d258 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,6 +20,7 @@
 
 #include "SkiaRenderEngine.h"
 
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <GrBackendSemaphore.h>
 #include <GrContextOptions.h>
 #include <SkBlendMode.h>
@@ -1109,7 +1110,7 @@
         }
         if (kFlushAfterEveryLayer) {
             ATRACE_NAME("flush surface");
-            activeSurface->flush();
+            skgpu::ganesh::Flush(activeSurface);
         }
     }
     for (const auto& borderRenderInfo : display.borderInfoList) {
@@ -1137,7 +1138,7 @@
     {
         ATRACE_NAME("flush surface");
         LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
-        activeSurface->flush();
+        skgpu::ganesh::Flush(activeSurface);
     }
 
     auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 2557ac9..1e0c4cf 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -16,6 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #include "BlurFilter.h"
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkPaint.h>
 #include <SkRRect.h>
@@ -23,6 +24,7 @@
 #include <SkSize.h>
 #include <SkString.h>
 #include <SkSurface.h>
+#include <SkTileMode.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index 511d7c9..e72c501 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -17,6 +17,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "GaussianBlurFilter.h"
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkPaint.h>
 #include <SkRRect.h>
@@ -25,6 +26,8 @@
 #include <SkSize.h>
 #include <SkString.h>
 #include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include "include/gpu/GpuTypes.h" // from Skia
 #include <log/log.h>
 #include <utils/Trace.h>
@@ -45,8 +48,8 @@
     // Create blur surface with the bit depth and colorspace of the original surface
     SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
                                                        std::ceil(blurRect.height() * kInputScale));
-    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context,
-                                                           skgpu::Budgeted::kNo, scaledInfo);
+    sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+                                                        skgpu::Budgeted::kNo, scaledInfo);
 
     SkPaint paint;
     paint.setBlendMode(SkBlendMode::kSrc);
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index e370c39..0c7335c 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -17,13 +17,20 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "KawaseBlurFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
+#include <SkImageInfo.h>
 #include <SkPaint.h>
 #include <SkRRect.h>
 #include <SkRuntimeEffect.h>
+#include <SkShader.h>
 #include <SkSize.h>
 #include <SkString.h>
 #include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
@@ -32,19 +39,18 @@
 namespace skia {
 
 KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
-    SkString blurString(R"(
-        uniform shader child;
-        uniform float in_blurOffset;
+    SkString blurString(
+        "uniform shader child;"
+        "uniform float in_blurOffset;"
 
-        half4 main(float2 xy) {
-            half4 c = child.eval(xy);
-            c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
-            c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
-            c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
-            c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
-            return half4(c.rgb * 0.2, 1.0);
-        }
-    )");
+        "half4 main(float2 xy) {"
+            "half4 c = child.eval(xy);"
+            "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));"
+            "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));"
+            "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));"
+            "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));"
+            "return half4(c.rgb * 0.2, 1.0);"
+        "}");
 
     auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
     if (!blurEffect) {
@@ -53,9 +59,44 @@
     mBlurEffect = std::move(blurEffect);
 }
 
-sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
-                                          const sk_sp<SkImage> input, const SkRect& blurRect)
-    const {
+// Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an
+// SkImage.
+static sk_sp<SkImage> makeImage(GrRecordingContext* context, SkRuntimeShaderBuilder* builder,
+                                const SkImageInfo& resultInfo) {
+    if (resultInfo.alphaType() == kUnpremul_SkAlphaType ||
+        resultInfo.alphaType() == kUnknown_SkAlphaType) {
+        return nullptr;
+    }
+    constexpr int kSampleCount = 1;
+    constexpr bool kMipmapped = false;
+
+    sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+                                                        skgpu::Budgeted::kYes,
+                                                        resultInfo,
+                                                        kSampleCount,
+                                                        kTopLeft_GrSurfaceOrigin,
+                                                        nullptr,
+                                                        kMipmapped);
+    if (!surface) {
+        return nullptr;
+    }
+    sk_sp<SkShader> shader = builder->makeShader(nullptr);
+    if (!shader) {
+        return nullptr;
+    }
+    SkPaint paint;
+    paint.setShader(std::move(shader));
+    paint.setBlendMode(SkBlendMode::kSrc);
+    surface->getCanvas()->drawPaint(paint);
+    return surface->makeImageSnapshot();
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
+                                          const uint32_t blurRadius,
+                                          const sk_sp<SkImage> input,
+                                          const SkRect& blurRect) const {
+    LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
+    LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__);
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -80,14 +121,15 @@
             input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
     blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
 
-    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+    sk_sp<SkImage> tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
 
     // And now we'll build our chain of scaled blur stages
     for (auto i = 1; i < numberOfPasses; i++) {
+        LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
         blurBuilder.child("child") =
                 tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
         blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
-        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+        tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
     }
 
     return tmpBlur;
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 019d6cb..634d35a 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -64,6 +64,14 @@
         Sensor s;
         Vector<Sensor> v;
         uint32_t n = reply.readUint32();
+        // The size of the n Sensor elements on the wire is what we really want, but
+        // this is better than nothing.
+        if (n > reply.dataAvail()) {
+            ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+                  "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+                  n, reply.dataAvail());
+            return v;
+        }
         v.setCapacity(n);
         while (n) {
             n--;
@@ -86,6 +94,14 @@
         Sensor s;
         Vector<Sensor> v;
         uint32_t n = reply.readUint32();
+        // The size of the n Sensor elements on the wire is what we really want, but
+        // this is better than nothing.
+        if (n > reply.dataAvail()) {
+            ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+                  "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+                  n, reply.dataAvail());
+            return v;
+        }
         v.setCapacity(n);
         while (n) {
             n--;
@@ -109,6 +125,14 @@
         Sensor s;
         Vector<Sensor> v;
         uint32_t n = reply.readUint32();
+        // The size of the n Sensor elements on the wire is what we really want, but
+        // this is better than nothing.
+        if (n > reply.dataAvail()) {
+            ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+                  "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+                  n, reply.dataAvail());
+            return v;
+        }
         v.setCapacity(n);
         while (n) {
             n--;
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index 2be98e7..57c74ee 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -32,27 +32,12 @@
 sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService()
 {
     std::lock_guard<Mutex> scoped_lock(mLock);
-    int64_t startTime = 0;
     sp<hardware::ISensorPrivacyManager> service = mService;
-    while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
-        sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy"));
-        if (binder == nullptr) {
-            // Wait for the sensor privacy service to come back...
-            if (startTime == 0) {
-                startTime = uptimeMillis();
-                ALOGI("Waiting for sensor privacy service");
-            } else if ((uptimeMillis() - startTime) > 1000000) {
-                ALOGW("Waiting too long for sensor privacy service, giving up");
-                service = nullptr;
-                break;
-            }
-            usleep(25000);
-        } else {
-            service = interface_cast<hardware::ISensorPrivacyManager>(binder);
-            mService = service;
-        }
+    if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+      sp<IBinder> binder = defaultServiceManager()->waitForService(String16("sensor_privacy"));
+      mService = interface_cast<hardware::ISensorPrivacyManager>(binder);
     }
-    return service;
+    return mService;
 }
 
 bool SensorPrivacyManager::supportsSensorToggle(int toggleType, int sensor) {
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index dbe475b..f859848 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -36,6 +36,15 @@
 
 #include <hardware/gralloc.h>
 
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+// TODO: Provide alternatives that aren't broken
+#define AHB_CONVERSION                                                                          \
+    [[deprecated("WARNING: VNDK casts beteween GraphicBuffer & AHardwareBuffer are UNSAFE and " \
+                 "will be removed in the future")]]
+#else
+#define AHB_CONVERSION
+#endif
+
 namespace android {
 
 class GraphicBufferMapper;
@@ -80,10 +89,10 @@
 
     static sp<GraphicBuffer> from(ANativeWindowBuffer *);
 
-    static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*);
-    static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*);
-    AHardwareBuffer* toAHardwareBuffer();
-    AHardwareBuffer const* toAHardwareBuffer() const;
+    AHB_CONVERSION static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*);
+    AHB_CONVERSION static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*);
+    AHB_CONVERSION AHardwareBuffer* toAHardwareBuffer();
+    AHB_CONVERSION AHardwareBuffer const* toAHardwareBuffer() const;
 
     // Create a GraphicBuffer to be unflatten'ed into or be reallocated.
     GraphicBuffer();
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
index 1ab3c7c..e41b645 100644
--- a/libs/ultrahdr/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -19,7 +19,6 @@
 #endif
 
 #include <ultrahdr/icc.h>
-#include <ultrahdr/gainmapmath.h>
 #include <vector>
 #include <utils/Log.h>
 
@@ -218,8 +217,8 @@
     total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
     sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(kTAG_CurveType));     // Type
-    dataStruct->write32(0);                                     // Reserved
-    dataStruct->write32(Endian_SwapBE32(table_entries));  // Value count
+    dataStruct->write32(0);                                   // Reserved
+    dataStruct->write32(Endian_SwapBE32(table_entries));      // Value count
     for (size_t i = 0; i < table_entries; ++i) {
         uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
         dataStruct->write16(value);
@@ -227,14 +226,30 @@
     return dataStruct;
 }
 
-sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
-    int total_length = 16;
-    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
-    dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
-    dataStruct->write32(0);                                      // Reserved
-    dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
-    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0)));
+sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
+    if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
+            && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
+        int total_length = 16;
+        sp<DataStruct> dataStruct = new DataStruct(total_length);
+        dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
+        dataStruct->write32(0);                                    // Reserved
+        dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+        dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+        return dataStruct;
+    }
 
+    int total_length = 40;
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
+    dataStruct->write32(0);                                    // Reserved
+    dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
     return dataStruct;
 }
 
@@ -349,7 +364,7 @@
 
     // The "B" curve is required.
     for (size_t i = 0; i < kNumChannels; ++i) {
-        b_curves_data[i] = write_trc_tag_for_linear();
+        b_curves_data[i] = write_trc_tag(kLinear_TransFun);
     }
 
     // The "A" curve and CLUT are optional.
@@ -362,7 +377,7 @@
 
         a_curves_offset = clut_offset + clut->getLength();
         for (size_t i = 0; i < kNumChannels; ++i) {
-            a_curves_data[i] = write_trc_tag_for_linear();
+            a_curves_data[i] = write_trc_tag(kLinear_TransFun);
         }
     }
 
@@ -460,9 +475,9 @@
             tags.emplace_back(kTAG_bTRC,
                     write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
         } else {
-            tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear());
-            tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear());
-            tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear());
+            tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
+            tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
+            tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
         }
     }
 
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index edf152d..50b4d2f 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -51,6 +51,23 @@
 typedef Color (*ColorTransformFn)(Color);
 typedef float (*ColorCalculationFn)(Color);
 
+// A transfer function mapping encoded values to linear values,
+// represented by this 7-parameter piecewise function:
+//
+//   linear = sign(encoded) *  (c*|encoded| + f)       , 0 <= |encoded| < d
+//          = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
+//
+// (A simple gamma transfer function sets g to gamma and a to 1.)
+typedef struct TransferFunction {
+    float g, a,b,c,d,e,f;
+} TransferFunction;
+
+static constexpr TransferFunction kSRGB_TransFun =
+    { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+
+static constexpr TransferFunction kLinear_TransFun =
+    { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
 inline Color operator+=(Color& lhs, const Color& rhs) {
   lhs.r += rhs.r;
   lhs.g += rhs.g;
@@ -312,7 +329,7 @@
 float hlgOetfLUT(float e);
 Color hlgOetfLUT(Color e);
 
-constexpr size_t kHlgOETFPrecision = 10;
+constexpr size_t kHlgOETFPrecision = 16;
 constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
 
 /*
@@ -325,7 +342,7 @@
 float hlgInvOetfLUT(float e_gamma);
 Color hlgInvOetfLUT(Color e_gamma);
 
-constexpr size_t kHlgInvOETFPrecision = 10;
+constexpr size_t kHlgInvOETFPrecision = 12;
 constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
 
 /*
@@ -338,7 +355,7 @@
 float pqOetfLUT(float e);
 Color pqOetfLUT(Color e);
 
-constexpr size_t kPqOETFPrecision = 10;
+constexpr size_t kPqOETFPrecision = 16;
 constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
 
 /*
@@ -351,7 +368,7 @@
 float pqInvOetfLUT(float e_gamma);
 Color pqInvOetfLUT(Color e_gamma);
 
-constexpr size_t kPqInvOETFPrecision = 10;
+constexpr size_t kPqInvOETFPrecision = 12;
 constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
 
 
diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
index 7f047f8..971b267 100644
--- a/libs/ultrahdr/include/ultrahdr/icc.h
+++ b/libs/ultrahdr/include/ultrahdr/icc.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_ULTRAHDR_ICC_H
 #define ANDROID_ULTRAHDR_ICC_H
 
+#include <ultrahdr/gainmapmath.h>
 #include <ultrahdr/jpegr.h>
 #include <ultrahdr/jpegrutils.h>
 #include <utils/RefBase.h>
@@ -222,7 +223,7 @@
                                        const ultrahdr_color_gamut gamut);
     static sp<DataStruct> write_xyz_tag(float x, float y, float z);
     static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
-    static sp<DataStruct> write_trc_tag_for_linear();
+    static sp<DataStruct> write_trc_tag(const TransferFunction& fn);
     static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
     static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
                                          uint32_t transfer_characteristics);
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
index a35fd30..f80496a 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -348,16 +348,6 @@
     status_t extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
                                            jr_compressed_ptr primary_image,
                                            jr_compressed_ptr gain_map);
-    /*
-     * This method is called in the decoding pipeline. It will read XMP metadata to find the start
-     * position of the compressed gain map, and will extract the compressed gain map.
-     *
-     * @param compressed_jpegr_image compressed JPEGR image
-     * @param dest destination of compressed gain map
-     * @return NO_ERROR if calculation succeeds, error code if error occurs.
-     */
-    status_t extractGainMap(jr_compressed_ptr compressed_jpegr_image,
-                            jr_compressed_ptr dest);
 
     /*
      * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
index 0641232..5420e1c 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
@@ -44,6 +44,7 @@
     ERROR_JPEGR_INVALID_TRANS_FUNC      = JPEGR_IO_ERROR_BASE - 6,
     ERROR_JPEGR_INVALID_METADATA        = JPEGR_IO_ERROR_BASE - 7,
     ERROR_JPEGR_UNSUPPORTED_METADATA    = JPEGR_IO_ERROR_BASE - 8,
+    ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9,
 
     JPEGR_RUNTIME_ERROR_BASE            = -20000,
     ERROR_JPEGR_ENCODE_ERROR            = JPEGR_RUNTIME_ERROR_BASE - 1,
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 9c57f34..5a601bd 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -539,9 +539,12 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  jpegr_compressed_struct primary_image, gain_map;
-  JPEGR_CHECK(extractPrimaryImageAndGainMap(compressed_jpegr_image,
-                                            &primary_image, &gain_map));
+  jpegr_compressed_struct primary_image, gainmap_image;
+  status_t status =
+      extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+  if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+    return status;
+  }
 
   JpegDecoderHelper jpeg_decoder;
   if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
@@ -550,7 +553,7 @@
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
-  return NO_ERROR;
+  return status;
 }
 
 /* Decode API */
@@ -586,90 +589,34 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  if (output_format == ULTRAHDR_OUTPUT_SDR) {
-    JpegDecoderHelper jpeg_decoder;
-    if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
-                                      true)) {
-        return ERROR_JPEGR_DECODE_ERROR;
+  jpegr_compressed_struct primary_image, gainmap_image;
+  status_t status =
+      extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+  if (status != NO_ERROR) {
+    if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+      ALOGE("received invalid compressed jpegr image");
+      return status;
     }
-    jpegr_uncompressed_struct uncompressed_rgba_image;
-    uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
-    uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
-    uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
-    memcpy(dest->data, uncompressed_rgba_image.data,
-           uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
-    dest->width = uncompressed_rgba_image.width;
-    dest->height = uncompressed_rgba_image.height;
-
-    if (gain_map == nullptr && exif == nullptr) {
-      return NO_ERROR;
-    }
-
-    if (exif != nullptr) {
-      if (exif->data == nullptr) {
-        return ERROR_JPEGR_INVALID_NULL_PTR;
-      }
-      if (exif->length < jpeg_decoder.getEXIFSize()) {
-        return ERROR_JPEGR_BUFFER_TOO_SMALL;
-      }
-      memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
-      exif->length = jpeg_decoder.getEXIFSize();
-    }
-    if (gain_map == nullptr) {
-      return NO_ERROR;
-    }
-  }
-
-  jpegr_compressed_struct compressed_map;
-  JPEGR_CHECK(extractGainMap(compressed_jpegr_image, &compressed_map));
-
-  JpegDecoderHelper gain_map_decoder;
-  if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
-    return ERROR_JPEGR_DECODE_ERROR;
-  }
-  if ((gain_map_decoder.getDecompressedImageWidth() *
-       gain_map_decoder.getDecompressedImageHeight()) >
-      gain_map_decoder.getDecompressedImageSize()) {
-    return ERROR_JPEGR_CALCULATION_ERROR;
-  }
-
-  if (gain_map != nullptr) {
-    gain_map->width = gain_map_decoder.getDecompressedImageWidth();
-    gain_map->height = gain_map_decoder.getDecompressedImageHeight();
-    int size = gain_map->width * gain_map->height;
-    gain_map->data = malloc(size);
-    memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size);
-  }
-
-  ultrahdr_metadata_struct uhdr_metadata;
-  if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
-                          gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
-    return ERROR_JPEGR_INVALID_METADATA;
-  }
-
-  if (metadata != nullptr) {
-      metadata->version = uhdr_metadata.version;
-      metadata->minContentBoost = uhdr_metadata.minContentBoost;
-      metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
-      metadata->gamma = uhdr_metadata.gamma;
-      metadata->offsetSdr = uhdr_metadata.offsetSdr;
-      metadata->offsetHdr = uhdr_metadata.offsetHdr;
-      metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
-      metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
-  }
-
-  if (output_format == ULTRAHDR_OUTPUT_SDR) {
-    return NO_ERROR;
   }
 
   JpegDecoderHelper jpeg_decoder;
-  if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
+  if (!jpeg_decoder.decompressImage(primary_image.data, primary_image.length,
+                                    (output_format == ULTRAHDR_OUTPUT_SDR))) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
-  if ((jpeg_decoder.getDecompressedImageWidth() *
-       jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
-      jpeg_decoder.getDecompressedImageSize()) {
-    return ERROR_JPEGR_CALCULATION_ERROR;
+
+  if (output_format == ULTRAHDR_OUTPUT_SDR) {
+    if ((jpeg_decoder.getDecompressedImageWidth() *
+         jpeg_decoder.getDecompressedImageHeight() * 4) >
+        jpeg_decoder.getDecompressedImageSize()) {
+      return ERROR_JPEGR_CALCULATION_ERROR;
+    }
+  } else {
+    if ((jpeg_decoder.getDecompressedImageWidth() *
+         jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
+        jpeg_decoder.getDecompressedImageSize()) {
+      return ERROR_JPEGR_CALCULATION_ERROR;
+    }
   }
 
   if (exif != nullptr) {
@@ -683,11 +630,53 @@
     exif->length = jpeg_decoder.getEXIFSize();
   }
 
+  if (output_format == ULTRAHDR_OUTPUT_SDR) {
+    dest->width = jpeg_decoder.getDecompressedImageWidth();
+    dest->height = jpeg_decoder.getDecompressedImageHeight();
+    memcpy(dest->data, jpeg_decoder.getDecompressedImagePtr(), dest->width * dest->height * 4);
+    return NO_ERROR;
+  }
+
+  JpegDecoderHelper gain_map_decoder;
+  if (!gain_map_decoder.decompressImage(gainmap_image.data, gainmap_image.length)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+  if ((gain_map_decoder.getDecompressedImageWidth() *
+       gain_map_decoder.getDecompressedImageHeight()) >
+      gain_map_decoder.getDecompressedImageSize()) {
+    return ERROR_JPEGR_CALCULATION_ERROR;
+  }
+
   jpegr_uncompressed_struct map;
   map.data = gain_map_decoder.getDecompressedImagePtr();
   map.width = gain_map_decoder.getDecompressedImageWidth();
   map.height = gain_map_decoder.getDecompressedImageHeight();
 
+  if (gain_map != nullptr) {
+    gain_map->width = map.width;
+    gain_map->height = map.height;
+    int size = gain_map->width * gain_map->height;
+    gain_map->data = malloc(size);
+    memcpy(gain_map->data, map.data, size);
+  }
+
+  ultrahdr_metadata_struct uhdr_metadata;
+  if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
+                          gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
+    return ERROR_JPEGR_INVALID_METADATA;
+  }
+
+  if (metadata != nullptr) {
+    metadata->version = uhdr_metadata.version;
+    metadata->minContentBoost = uhdr_metadata.minContentBoost;
+    metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
+    metadata->gamma = uhdr_metadata.gamma;
+    metadata->offsetSdr = uhdr_metadata.offsetSdr;
+    metadata->offsetHdr = uhdr_metadata.offsetHdr;
+    metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
+    metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
+  }
+
   jpegr_uncompressed_struct uncompressed_yuv_420_image;
   uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
@@ -1070,7 +1059,7 @@
             }
             case ULTRAHDR_OUTPUT_HDR_PQ:
             {
-#if USE_HLG_OETF_LUT
+#if USE_PQ_OETF_LUT
               ColorTransformFn hdrOetf = pqOetfLUT;
 #else
               ColorTransformFn hdrOetf = pqOetf;
@@ -1131,12 +1120,8 @@
 
   const auto& jpeg_info = jpeg_info_builder.GetInfo();
   const auto& image_ranges = jpeg_info.GetImageRanges();
-  if (image_ranges.empty()) {
-    return ERROR_JPEGR_INVALID_INPUT_TYPE;
-  }
 
-  if (image_ranges.size() != 2) {
-    // Must be 2 JPEG Images
+  if (image_ranges.empty()) {
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
@@ -1146,23 +1131,23 @@
     primary_image->length = image_ranges[0].GetLength();
   }
 
+  if (image_ranges.size() == 1) {
+    return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND;
+  }
+
   if (gain_map != nullptr) {
     gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
                                               image_ranges[1].GetBegin();
     gain_map->length = image_ranges[1].GetLength();
   }
 
-  return NO_ERROR;
-}
-
-
-status_t JpegR::extractGainMap(jr_compressed_ptr compressed_jpegr_image,
-                               jr_compressed_ptr dest) {
-  if (compressed_jpegr_image == nullptr || dest == nullptr) {
-    return ERROR_JPEGR_INVALID_NULL_PTR;
+  // TODO: choose primary image and gain map image carefully
+  if (image_ranges.size() > 2) {
+    ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen",
+          (int)image_ranges.size());
   }
 
-  return extractPrimaryImageAndGainMap(compressed_jpegr_image, nullptr, dest);
+  return NO_ERROR;
 }
 
 // JPEG/R structure:
diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp
index 8681784..c88f2fc 100644
--- a/services/displayservice/Android.bp
+++ b/services/displayservice/Android.bp
@@ -23,7 +23,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library_static {
     name: "libdisplayservicehidl",
 
     srcs: [
@@ -37,18 +37,24 @@
         "libgui",
         "libhidlbase",
         "libutils",
+    ],
+
+    static_libs: [
         "android.frameworks.displayservice@1.0",
     ],
 
     export_include_dirs: ["include"],
     export_shared_lib_headers: [
-        "android.frameworks.displayservice@1.0",
         "libgui",
         "libutils",
     ],
 
+    export_static_lib_headers: [
+        "android.frameworks.displayservice@1.0",
+    ],
+
     cflags: [
         "-Werror",
         "-Wall",
-    ]
+    ],
 }
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index dc7c75a..f749b0e 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -28,6 +28,7 @@
 
 cc_defaults {
     name: "inputflinger_defaults",
+    host_supported: true,
     cpp_std: "c++20",
     cflags: [
         "-Wall",
@@ -50,6 +51,20 @@
         "-*", // Disable all checks not explicitly enabled for now
     ] + inputflinger_tidy_checks,
     tidy_checks_as_errors: inputflinger_tidy_checks,
+    target: {
+        host: {
+            sanitize: {
+                address: true,
+            },
+            include_dirs: [
+                "bionic/libc/kernel/android/uapi/",
+                "bionic/libc/kernel/uapi",
+            ],
+            cflags: [
+                "-D__ANDROID_HOST__",
+            ],
+        },
+    },
 }
 
 /////////////////////////////////////////////////
@@ -79,6 +94,7 @@
         "libcrypto",
         "libcutils",
         "libhidlbase",
+        "libinput",
         "libkll",
         "liblog",
         "libprotobuf-cpp-lite",
@@ -95,30 +111,22 @@
         android: {
             shared_libs: [
                 "libgui",
-                "libinput",
                 "libstatspull",
                 "libstatssocket",
             ],
         },
         host: {
             static_libs: [
-                "libinput",
                 "libstatspull",
                 "libstatssocket",
             ],
-            include_dirs: [
-                "bionic/libc/kernel/android/uapi/",
-                "bionic/libc/kernel/uapi",
-            ],
-            cflags: [
-                "-D__ANDROID_HOST__",
-            ],
         },
     },
 }
 
 cc_library_shared {
     name: "libinputflinger",
+    host_supported: true,
     defaults: [
         "inputflinger_defaults",
         "libinputflinger_defaults",
@@ -179,25 +187,13 @@
         "libbase",
         "libbinder",
         "libcutils",
+        "libinput",
         "liblog",
         "libutils",
     ],
     header_libs: [
         "libinputflinger_headers",
     ],
-    target: {
-        android: {
-            shared_libs: [
-                "libinput",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libinput",
-                "libui-types",
-            ],
-        },
-    },
 }
 
 cc_library_shared {
@@ -224,6 +220,7 @@
         // native targets
         "libgui_test",
         "libinput",
+        "libinputreader_static",
         "libinputflinger",
         "inputflinger_tests",
         "inputflinger_benchmarks",
@@ -246,6 +243,7 @@
         "inputflinger_keyboard_input_fuzzer",
         "inputflinger_multitouch_input_fuzzer",
         "inputflinger_switch_input_fuzzer",
+        "inputflinger_touchpad_input_fuzzer",
         "inputflinger_input_reader_fuzzer",
         "inputflinger_blocking_queue_fuzzer",
         "inputflinger_input_classifier_fuzzer",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 37b3187..c903564 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -186,7 +186,7 @@
 
     dump += " InputFlinger dump\n";
 
-    ::write(fd, dump.c_str(), dump.size());
+    TEMP_FAILURE_RETRY(::write(fd, dump.c_str(), dump.size()));
     return NO_ERROR;
 }
 
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 0fa47d1..c34cd53 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -91,8 +91,8 @@
         readTime(readTime),
         videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties.push_back(pointerProperties[i]);
-        this->pointerCoords.push_back(pointerCoords[i]);
+        this->pointerProperties.emplace_back(pointerProperties[i]);
+        this->pointerCoords.emplace_back(pointerCoords[i]);
     }
 }
 
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index cdc4c08..ca00e7d 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -116,6 +116,14 @@
       ]
     },
     {
+      "name": "CtsAppTestCases",
+      "options": [
+        {
+          "include-filter": "android.app.cts.ToolbarActionBarTest"
+        }
+      ]
+    },
+    {
       "name": "FrameworksServicesTests",
       "options": [
         {
@@ -246,6 +254,14 @@
       ]
     },
     {
+      "name": "CtsAppTestCases",
+      "options": [
+        {
+          "include-filter": "android.app.cts.ToolbarActionBarTest"
+        }
+      ]
+    },
+    {
       "name": "FrameworksServicesTests",
       "options": [
         {
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 4e2a6fb..e200f8b 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -19,6 +19,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcrypto",
         "libcutils",
         "libinputflinger_base",
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b2e274d..7a41083 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -82,8 +82,6 @@
 
     void notifyVibratorState(int32_t deviceId, bool isOn) override {}
 
-    InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
     bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
         return true; // dispatch event normally
     }
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 492551e..8b57730 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -59,6 +59,7 @@
         "libbase",
         "libcrypto",
         "libcutils",
+        "libinput",
         "libkll",
         "liblog",
         "libprotobuf-cpp-lite",
@@ -74,14 +75,12 @@
         android: {
             shared_libs: [
                 "libgui",
-                "libinput",
                 "libstatspull",
                 "libstatssocket",
             ],
         },
         host: {
             static_libs: [
-                "libinput",
                 "libstatspull",
                 "libstatssocket",
             ],
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a670ebe..cb369a8 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -235,8 +235,8 @@
         downTime(downTime),
         pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties[i].copyFrom(pointerProperties[i]);
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+        this->pointerProperties[i] = pointerProperties[i];
+        this->pointerCoords[i] = pointerCoords[i];
     }
 }
 
@@ -321,7 +321,28 @@
         globalScaleFactor(globalScaleFactor),
         deliveryTime(0),
         resolvedAction(0),
-        resolvedFlags(0) {}
+        resolvedFlags(0) {
+    switch (this->eventEntry->type) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<KeyEntry&>(*this->eventEntry);
+            resolvedEventId = keyEntry.id;
+            resolvedAction = keyEntry.action;
+            resolvedFlags = keyEntry.flags;
+
+            break;
+        }
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<MotionEntry&>(*this->eventEntry);
+            resolvedEventId = motionEntry.id;
+            resolvedAction = motionEntry.action;
+            resolvedFlags = motionEntry.flags;
+            break;
+        }
+        default: {
+            break;
+        }
+    }
+}
 
 uint32_t DispatchEntry::nextSeq() {
     // Sequence number 0 is reserved and will never be returned.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3545b98..a6aa8a1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2690,9 +2690,6 @@
                      "Conflicting pointer actions: Hover received while pointer was down.");
             *outConflictingPointerActions = true;
         }
-        if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-            maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-        }
     } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
         // Pointer went up.
         tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
@@ -2980,7 +2977,7 @@
             !haveSameApplicationToken(windowInfo, otherInfo)) {
             if (DEBUG_TOUCH_OCCLUSION) {
                 info.debugInfo.push_back(
-                        dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+                        dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false));
             }
             // canBeObscuredBy() has returned true above, which means this window is untrusted, so
             // we perform the checks below to see if the touch can be propagated or not based on the
@@ -3008,8 +3005,7 @@
         }
     }
     if (DEBUG_TOUCH_OCCLUSION) {
-        info.debugInfo.push_back(
-                dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+        info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true));
     }
     return info;
 }
@@ -3303,10 +3299,6 @@
     switch (newEntry.type) {
         case EventEntry::Type::KEY: {
             const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
-            dispatchEntry->resolvedEventId = keyEntry.id;
-            dispatchEntry->resolvedAction = keyEntry.action;
-            dispatchEntry->resolvedFlags = keyEntry.flags;
-
             if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags)) {
                 LOG(WARNING) << "channel " << connection->getInputChannelName()
@@ -3334,7 +3326,6 @@
             } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
             } else {
-                dispatchEntry->resolvedAction = motionEntry.action;
                 dispatchEntry->resolvedEventId = motionEntry.id;
             }
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
@@ -3350,7 +3341,6 @@
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
             }
 
-            dispatchEntry->resolvedFlags = motionEntry.flags;
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
                 dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
             }
@@ -4094,9 +4084,9 @@
         uint32_t pointerId = uint32_t(pointerProperties.id);
         if (pointerIds.test(pointerId)) {
             splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
-            splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
-            splitPointerCoords[splitPointerCount].copyFrom(
-                    originalMotionEntry.pointerCoords[originalPointerIndex]);
+            splitPointerProperties[splitPointerCount] = pointerProperties;
+            splitPointerCoords[splitPointerCount] =
+                    originalMotionEntry.pointerCoords[originalPointerIndex];
             splitPointerCount += 1;
         }
     }
@@ -4354,7 +4344,7 @@
             validateMotionEvent(args.action, args.actionButton, args.getPointerCount(),
                                 args.pointerProperties.data());
     if (!motionCheck.ok()) {
-        LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
+        LOG(FATAL) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
         return;
     }
 
@@ -4513,6 +4503,10 @@
         std::unique_ptr<DeviceResetEntry> newEntry =
                 std::make_unique<DeviceResetEntry>(args.id, args.eventTime, args.deviceId);
         needWake = enqueueInboundEventLocked(std::move(newEntry));
+
+        for (auto& [_, verifier] : mVerifiersByDisplay) {
+            verifier.resetDevice(args.deviceId);
+        }
     } // release lock
 
     if (needWake) {
@@ -5130,13 +5124,6 @@
     // Copy old handles for release if they are no longer present.
     const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
 
-    // Save the old windows' orientation by ID before it gets updated.
-    std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
-    for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
-        oldWindowOrientations.emplace(handle->getId(),
-                                      handle->getInfo()->transform.getOrientation());
-    }
-
     updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
 
     const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -5190,23 +5177,6 @@
         }
     }
 
-    // Determine if the orientation of any of the input windows have changed, and cancel all
-    // pointer events if necessary.
-    for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
-        const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
-        if (newWindowHandle != nullptr &&
-            newWindowHandle->getInfo()->transform.getOrientation() !=
-                    oldWindowOrientations[oldWindowHandle->getId()]) {
-            std::shared_ptr<InputChannel> inputChannel =
-                    getInputChannelLocked(newWindowHandle->getToken());
-            if (inputChannel != nullptr) {
-                CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
-                                           "touched window's orientation changed");
-                synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
-            }
-        }
-    }
-
     // Release information for windows that are no longer present.
     // This ensures that unused input channels are released promptly.
     // Otherwise, they might stick around until the window handle is destroyed
@@ -6725,7 +6695,7 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
         // Set an empty list to remove all handles from the specific display.
-        setInputWindowsLocked(/* window handles */ {}, displayId);
+        setInputWindowsLocked(/*windowInfoHandles=*/{}, displayId);
         setFocusedApplicationLocked(displayId, nullptr);
         // Call focus resolver to clean up stale requests. This must be called after input windows
         // have been removed for the removed display.
@@ -6814,13 +6784,6 @@
     mLooper->wake();
 }
 
-void InputDispatcher::requestRefreshConfiguration() {
-    InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration();
-
-    std::scoped_lock _l(mLock);
-    mConfig = config;
-}
-
 void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) {
     std::scoped_lock _l(mLock);
     mMonitorDispatchingTimeout = timeout;
@@ -6933,4 +6896,11 @@
     return nullptr;
 }
 
+void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+    std::scoped_lock _l(mLock);
+
+    mConfig.keyRepeatTimeout = timeout;
+    mConfig.keyRepeatDelay = delay;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 37f569e..9cd1666 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -148,11 +148,11 @@
 
     void cancelCurrentTouch() override;
 
-    void requestRefreshConfiguration() override;
-
     // Public to allow tests to verify that a Monitor can get ANR.
     void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
 
+    void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 2fcb89a..e6941ef 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -28,7 +28,7 @@
 
 InputState::~InputState() {}
 
-bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
+bool InputState::isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const {
     for (const MotionMemento& memento : mMotionMementos) {
         if (memento.deviceId == deviceId && memento.source == source &&
             memento.displayId == displayId && memento.hovering) {
@@ -240,8 +240,8 @@
                 continue;
             }
         }
-        pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
-        pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
+        pointerProperties[pointerCount] = entry.pointerProperties[i];
+        pointerCoords[pointerCount] = entry.pointerCoords[i];
         pointerCount++;
     }
 }
@@ -251,8 +251,8 @@
         if (other.firstNewPointerIdx < 0) {
             other.firstNewPointerIdx = other.pointerCount;
         }
-        other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
-        other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+        other.pointerProperties[other.pointerCount] = pointerProperties[i];
+        other.pointerCoords[other.pointerCount] = pointerCoords[i];
         other.pointerCount++;
     }
 }
@@ -324,17 +324,16 @@
 
         // We will deliver all pointers the target already knows about
         for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
-            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
-            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerProperties[i] = memento.pointerProperties[i];
+            pointerCoords[i] = memento.pointerCoords[i];
             pointerCount++;
         }
 
         // We will send explicit events for all pointers the target doesn't know about
         for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
                 i < memento.pointerCount; i++) {
-
-            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
-            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerProperties[i] = memento.pointerProperties[i];
+            pointerCoords[i] = memento.pointerCoords[i];
             pointerCount++;
 
             // Down only if the first pointer, pointer down otherwise
@@ -370,8 +369,8 @@
     std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
     for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) {
         uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
-        pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]);
-        pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]);
+        pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
+        pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
         if (pointerIds.test(pointerId)) {
             canceledPointerIndices.push_back(pointerIdx);
         }
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index af2a3cb..e741137 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -36,7 +36,7 @@
 
     // Returns true if the specified source is known to have received a hover enter
     // motion event.
-    bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
+    bool isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const;
 
     // Records tracking information for a key event that has just been published.
     // Returns true if the event should be delivered, false if it is inconsistent
@@ -76,7 +76,7 @@
 
 private:
     struct KeyMemento {
-        int32_t deviceId;
+        DeviceId deviceId;
         uint32_t source;
         int32_t displayId;
         int32_t keyCode;
@@ -88,7 +88,7 @@
     };
 
     struct MotionMemento {
-        int32_t deviceId;
+        DeviceId deviceId;
         uint32_t source;
         int32_t displayId;
         int32_t flags;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index dadfdc1..4221e42 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -40,25 +40,25 @@
     return out;
 }
 
-bool TouchState::hasTouchingPointers(int32_t deviceId) const {
+bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
     return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
         return window.hasTouchingPointers(deviceId);
     });
 }
 
-void TouchState::removeTouchingPointer(int32_t removedDeviceId, int32_t pointerId) {
+void TouchState::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
     for (TouchedWindow& touchedWindow : windows) {
-        touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
+        touchedWindow.removeTouchingPointer(deviceId, pointerId);
     }
     clearWindowsWithoutPointers();
 }
 
 void TouchState::removeTouchingPointerFromWindow(
-        int32_t removedDeviceId, int32_t pointerId,
+        DeviceId deviceId, int32_t pointerId,
         const sp<android::gui::WindowInfoHandle>& windowHandle) {
     for (TouchedWindow& touchedWindow : windows) {
         if (touchedWindow.windowHandle == windowHandle) {
-            touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
+            touchedWindow.removeTouchingPointer(deviceId, pointerId);
             clearWindowsWithoutPointers();
             return;
         }
@@ -79,8 +79,7 @@
 }
 
 void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
-                                   ftl::Flags<InputTarget::Flags> targetFlags,
-                                   int32_t addedDeviceId,
+                                   ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
                                    std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
                                    std::optional<nsecs_t> firstDownTimeInTarget) {
     for (TouchedWindow& touchedWindow : windows) {
@@ -94,9 +93,9 @@
             // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
             // downTime set initially. Need to update existing window when a pointer is down for the
             // window.
-            touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+            touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
             if (firstDownTimeInTarget) {
-                touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
+                touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
             }
             return;
         }
@@ -104,25 +103,25 @@
     TouchedWindow touchedWindow;
     touchedWindow.windowHandle = windowHandle;
     touchedWindow.targetFlags = targetFlags;
-    touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+    touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
     if (firstDownTimeInTarget) {
-        touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
+        touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
     }
     windows.push_back(touchedWindow);
 }
 
 void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
-                                            int32_t hoveringDeviceId, int32_t hoveringPointerId) {
+                                            DeviceId deviceId, int32_t hoveringPointerId) {
     for (TouchedWindow& touchedWindow : windows) {
         if (touchedWindow.windowHandle == windowHandle) {
-            touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+            touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
             return;
         }
     }
 
     TouchedWindow touchedWindow;
     touchedWindow.windowHandle = windowHandle;
-    touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+    touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
     windows.push_back(touchedWindow);
 }
 
@@ -149,12 +148,12 @@
     }
 }
 
-void TouchState::cancelPointersForWindowsExcept(int32_t touchedDeviceId,
+void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
                                                 std::bitset<MAX_POINTER_ID + 1> pointerIds,
                                                 const sp<IBinder>& token) {
     std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
         if (w.windowHandle->getToken() != token) {
-            w.removeTouchingPointers(touchedDeviceId, pointerIds);
+            w.removeTouchingPointers(deviceId, pointerIds);
         }
     });
     clearWindowsWithoutPointers();
@@ -168,10 +167,10 @@
  */
 void TouchState::cancelPointersForNonPilferingWindows() {
     // First, find all pointers that are being pilfered, across all windows
-    std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
+    std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
     for (const TouchedWindow& w : windows) {
-        for (const auto& [iterDeviceId, pilferedPointerIds] : w.getPilferingPointers()) {
-            allPilferedPointerIdsByDevice[iterDeviceId] |= pilferedPointerIds;
+        for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
+            allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
         }
     };
 
@@ -183,12 +182,12 @@
     // (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
     // pilfered pointers will be disjoint across all windows, but there's no reason to cause that
     // limitation here.
-    for (const auto& [iterDeviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
+    for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
         std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
             std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
-                    w.getPilferingPointers(iterDeviceId) ^ allPilferedPointerIds;
+                    w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
             // Remove all pointers pilfered by other windows
-            w.removeTouchingPointers(iterDeviceId, pilferedByOtherWindows);
+            w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
         });
     }
     clearWindowsWithoutPointers();
@@ -248,11 +247,11 @@
                        [](const TouchedWindow& window) { return window.hasHoveringPointers(); });
 }
 
-std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
+std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
                                                                          int32_t pointerId) const {
     std::set<sp<WindowInfoHandle>> out;
     for (const TouchedWindow& window : windows) {
-        if (window.hasHoveringPointer(hoveringDeviceId, pointerId)) {
+        if (window.hasHoveringPointer(deviceId, pointerId)) {
             out.insert(window.windowHandle);
         }
     }
@@ -266,10 +265,10 @@
     clearWindowsWithoutPointers();
 }
 
-void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
+void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
     for (TouchedWindow& window : windows) {
-        window.removeAllHoveringPointersForDevice(removedDeviceId);
-        window.removeAllTouchingPointersForDevice(removedDeviceId);
+        window.removeAllHoveringPointersForDevice(deviceId);
+        window.removeAllTouchingPointersForDevice(deviceId);
     }
 
     clearWindowsWithoutPointers();
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9f29a4a..25b9643 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,24 +41,24 @@
     std::set<int32_t> getActiveDeviceIds() const;
 
     bool hasTouchingPointers(int32_t device) const;
-    void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
-    void removeTouchingPointerFromWindow(int32_t deviceId, int32_t pointerId,
+    void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
+    void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
                                          const sp<android::gui::WindowInfoHandle>& windowHandle);
     void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                           ftl::Flags<InputTarget::Flags> targetFlags, int32_t deviceId,
+                           ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
                            std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
                            std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
     void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                    int32_t deviceId, int32_t hoveringPointerId);
-    void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
+                                    DeviceId deviceId, int32_t hoveringPointerId);
+    void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
     void clearHoveringPointers();
 
-    void removeAllPointersForDevice(int32_t deviceId);
+    void removeAllPointersForDevice(DeviceId deviceId);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
 
     // Cancel pointers for current set of windows except the window with particular binder token.
-    void cancelPointersForWindowsExcept(int32_t deviceId,
+    void cancelPointersForWindowsExcept(DeviceId deviceId,
                                         std::bitset<MAX_POINTER_ID + 1> pointerIds,
                                         const sp<IBinder>& token);
     // Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
@@ -75,7 +75,7 @@
     bool hasHoveringPointers() const;
 
     std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
-            int32_t deviceId, int32_t pointerId) const;
+            DeviceId deviceId, int32_t pointerId) const;
     std::string dump() const;
 };
 
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 49597e2..f13885a 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -226,11 +226,10 @@
      */
     virtual void cancelCurrentTouch() = 0;
 
-    /**
-     * Request that the InputDispatcher's configuration, which can be obtained through the policy,
-     * be updated.
+    /*
+     * Updates key repeat configuration timeout and delay.
      */
-    virtual void requestRefreshConfiguration() = 0;
+    virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 729d01f..af28e48 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -73,9 +73,6 @@
                                       InputDeviceSensorAccuracy accuracy) = 0;
     virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
 
-    /* Gets the input dispatcher configuration. */
-    virtual InputDispatcherConfiguration getDispatcherConfiguration() = 0;
-
     /* Filters an input event.
      * Return true to dispatch the event unmodified, false to consume the event.
      * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ccb8773..c0c6d1f 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -52,6 +52,7 @@
         "mapper/RotaryEncoderInputMapper.cpp",
         "mapper/SensorInputMapper.cpp",
         "mapper/SingleTouchInputMapper.cpp",
+        "mapper/SlopController.cpp",
         "mapper/SwitchInputMapper.cpp",
         "mapper/TouchCursorInputMapperCommon.cpp",
         "mapper/TouchInputMapper.cpp",
@@ -79,6 +80,7 @@
         "libcrypto",
         "libcutils",
         "libjsoncpp",
+        "libinput",
         "liblog",
         "libPlatformProperties",
         "libstatslog",
@@ -97,13 +99,11 @@
     target: {
         android: {
             shared_libs: [
-                "libinput",
                 "libstatspull",
             ],
         },
         host: {
             static_libs: [
-                "libinput",
                 "libbinder",
                 "libstatspull",
             ],
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0a64a1c..2aaddf5 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -48,6 +48,7 @@
         mIdentifier(identifier),
         mClasses(0),
         mSources(0),
+        mIsWaking(false),
         mIsExternal(false),
         mHasMic(false),
         mDropUntilNextSync(false) {}
@@ -101,6 +102,7 @@
     dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
     dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
     dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+    dump += StringPrintf(INDENT2 "IsWaking: %s\n", toString(mIsWaking));
     dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
     if (mAssociatedDisplayPort) {
         dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
@@ -220,6 +222,7 @@
 
             mAssociatedDeviceType =
                     getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
+            mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
         }
 
         if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
@@ -376,9 +379,25 @@
         }
         --count;
     }
+    postProcess(out);
     return out;
 }
 
+void InputDevice::postProcess(std::list<NotifyArgs>& args) const {
+    if (mIsWaking) {
+        // Update policy flags to request wake for the `NotifyArgs` that come from waking devices.
+        for (auto& arg : args) {
+            if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+                notifyMotionArgs->policyFlags |= POLICY_FLAG_WAKE;
+            } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+                notifySwitchArgs->policyFlags |= POLICY_FLAG_WAKE;
+            } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+                notifyKeyArgs->policyFlags |= POLICY_FLAG_WAKE;
+            }
+        }
+    }
+}
+
 std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
     std::list<NotifyArgs> out;
     for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2f8e5bd..aae3fe7 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -191,6 +191,7 @@
     std::unique_ptr<PeripheralControllerInterface> mController;
 
     uint32_t mSources;
+    bool mIsWaking;
     bool mIsExternal;
     std::optional<uint8_t> mAssociatedDisplayPort;
     std::optional<std::string> mAssociatedDisplayUniqueId;
@@ -207,6 +208,10 @@
 
     PropertyMap mConfiguration;
 
+    // Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as
+    // per the properties of the InputDevice.
+    void postProcess(std::list<NotifyArgs>& args) const;
+
     // helpers to interate over the devices collection
     // run a function against every mapper on every subdevice
     inline void for_each_mapper(std::function<void(InputMapper&)> f) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index c684ed4..79f07a5 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -347,7 +347,7 @@
                                                AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                                &pointerCoords, mXPrecision, mYPrecision,
                                                xCursorPosition, yCursorPosition, downTime,
-                                               /* videoFrames */ {}));
+                                               /*videoFrames=*/{}));
             }
         }
 
@@ -357,7 +357,7 @@
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime,
-                                       /* videoFrames */ {}));
+                                       /*videoFrames=*/{}));
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
@@ -371,7 +371,7 @@
                                                AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                                &pointerCoords, mXPrecision, mYPrecision,
                                                xCursorPosition, yCursorPosition, downTime,
-                                               /* videoFrames */ {}));
+                                               /*videoFrames=*/{}));
             }
         }
 
@@ -386,7 +386,7 @@
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {}));
+                                           /*videoFrames=*/{}));
         }
 
         // Send scroll events.
@@ -401,7 +401,7 @@
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {}));
+                                           /*videoFrames=*/{}));
         }
     }
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 099a955..8a9ea75 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -352,7 +352,7 @@
                                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                    &pointerProperties, &pointerCoords, 0, 0,
                                    AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
     return out;
 }
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 7388752..e03a773 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -205,6 +205,7 @@
     int32_t keyCode;
     int32_t keyMetaState;
     uint32_t policyFlags;
+    int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
 
     if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
                                   &policyFlags)) {
@@ -226,6 +227,7 @@
             // key repeat, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[*keyDownIndex].keyCode;
             downTime = mKeyDowns[*keyDownIndex].downTime;
+            flags = mKeyDowns[*keyDownIndex].flags;
         } else {
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -234,12 +236,14 @@
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
                 out += getDeviceContext().cancelTouch(when, readTime);
+                flags |= AKEY_EVENT_FLAG_KEEP_TOUCH_MODE;
             }
 
             KeyDown keyDown;
             keyDown.keyCode = keyCode;
             keyDown.scanCode = scanCode;
             keyDown.downTime = when;
+            keyDown.flags = flags;
             mKeyDowns.push_back(keyDown);
         }
     } else {
@@ -248,6 +252,7 @@
             // key up, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[*keyDownIndex].keyCode;
             downTime = mKeyDowns[*keyDownIndex].downTime;
+            flags = mKeyDowns[*keyDownIndex].flags;
             mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
         } else {
             // key was not actually down
@@ -281,9 +286,8 @@
 
     out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                    mSource, getDisplayId(), policyFlags,
-                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                                   AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
-                                   downTime));
+                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
+                                   keyCode, scanCode, keyMetaState, downTime));
     return out;
 }
 
@@ -410,7 +414,7 @@
         out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
                                        systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
                                        getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP,
-                                       AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+                                       mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
                                        mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
                                        mKeyDowns[i].downTime));
     }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index cd3d3c4..45fd68b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -57,6 +57,7 @@
         nsecs_t downTime{};
         int32_t keyCode{};
         int32_t scanCode{};
+        int32_t flags{};
     };
 
     uint32_t mSource{};
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 13f2e59..07ae5b1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,7 @@
 
 #include "RotaryEncoderInputMapper.h"
 
+#include <utils/Timers.h>
 #include <optional>
 
 #include "CursorScrollAccumulator.h"
@@ -62,6 +63,7 @@
     dump += INDENT2 "Rotary Encoder Input Mapper:\n";
     dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
                          toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
+    dump += StringPrintf(INDENT3 "HaveSlopController: %s\n", toString(mSlopController != nullptr));
 }
 
 std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
@@ -70,6 +72,16 @@
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
     if (!changes.any()) {
         mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
+
+        const PropertyMap& propertyMap = getDeviceContext().getConfiguration();
+        float slopThreshold = propertyMap.getInt("rotary_encoder.slop_threshold").value_or(0);
+        int32_t slopDurationNs = milliseconds_to_nanoseconds(
+                propertyMap.getInt("rotary_encoder.slop_duration_ms").value_or(0));
+        if (slopThreshold > 0 && slopDurationNs > 0) {
+            mSlopController = std::make_unique<SlopController>(slopThreshold, slopDurationNs);
+        } else {
+            mSlopController = nullptr;
+        }
     }
     if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
@@ -103,6 +115,10 @@
     std::list<NotifyArgs> out;
 
     float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+    if (mSlopController) {
+        scroll = mSlopController->consumeEvent(when, scroll);
+    }
+
     bool scrolled = scroll != 0;
 
     // Send motion event.
@@ -132,10 +148,10 @@
         out.push_back(
                 NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                                  displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
-                                 metaState, /* buttonState */ 0, MotionClassification::NONE,
+                                 metaState, /*buttonState=*/0, MotionClassification::NONE,
                                  AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                  &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 9e2e8c4..fe5d152 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -20,6 +20,7 @@
 
 #include "CursorScrollAccumulator.h"
 #include "InputMapper.h"
+#include "SlopController.h"
 
 namespace android {
 
@@ -46,6 +47,7 @@
     int32_t mSource;
     float mScalingFactor;
     ui::Rotation mOrientation;
+    std::unique_ptr<SlopController> mSlopController;
 
     explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
                                       const InputReaderConfiguration& readerConfig);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
new file mode 100644
index 0000000..f79219f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 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.
+ */
+
+// clang-format off
+#include "../Macros.h"
+// clang-format on
+
+#include "SlopController.h"
+
+namespace {
+int signOf(float value) {
+    if (value == 0) return 0;
+    if (value > 0) return 1;
+    return -1;
+}
+} // namespace
+
+namespace android {
+
+SlopController::SlopController(float slopThreshold, nsecs_t slopDurationNanos)
+      : mSlopThreshold(slopThreshold), mSlopDurationNanos(slopDurationNanos) {}
+
+float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) {
+    if (mSlopDurationNanos == 0) {
+        return value;
+    }
+
+    if (shouldResetSlopTracking(eventTimeNanos, value)) {
+        mCumulativeValue = 0;
+        mHasSlopBeenMet = false;
+    }
+
+    mLastEventTimeNanos = eventTimeNanos;
+
+    if (mHasSlopBeenMet) {
+        // Since slop has already been met, we know that all of the current value would pass the
+        // slop threshold. So return that, without any further processing.
+        return value;
+    }
+
+    mCumulativeValue += value;
+
+    if (abs(mCumulativeValue) >= mSlopThreshold) {
+        mHasSlopBeenMet = true;
+        // Return the amount of value that exceeds the slop.
+        return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
+    }
+
+    return 0;
+}
+
+bool SlopController::shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const {
+    const nsecs_t ageNanos = eventTimeNanos - mLastEventTimeNanos;
+    if (ageNanos >= mSlopDurationNanos) {
+        return true;
+    }
+    if (value == 0) {
+        return false;
+    }
+    if (signOf(mCumulativeValue) != signOf(value)) {
+        return true;
+    }
+    return false;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SlopController.h b/services/inputflinger/reader/mapper/SlopController.h
new file mode 100644
index 0000000..c106410
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 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 <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Controls a slop logic. Slop here refers to an approach to try and drop insignificant input
+ * events. This is helpful in cases where unintentional input events may cause unintended outcomes,
+ * like scrolling a screen or keeping the screen awake.
+ *
+ * Current slop logic:
+ *      "If time since last event > Xns, then discard the next N values."
+ */
+class SlopController final {
+public:
+    SlopController(float slopThreshold, nsecs_t slopDurationNanos);
+
+    /**
+     * Consumes an event with a given time and value for slop processing.
+     * Returns an amount <=value that should be consumed.
+     */
+    float consumeEvent(nsecs_t eventTime, float value);
+
+private:
+    bool shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const;
+
+    /** The amount of event values ignored after an inactivity of the slop duration. */
+    const float mSlopThreshold;
+    /** The duration of inactivity that resets slop controlling. */
+    const nsecs_t mSlopDurationNanos;
+
+    nsecs_t mLastEventTimeNanos = 0;
+    float mCumulativeValue = 0;
+    bool mHasSlopBeenMet = false;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f2b0a4b..b565454 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -271,7 +271,7 @@
                          toString(mFusedStylusPointerId).c_str());
     dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
                          mExternalStylusFusionTimeout);
-    dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x",
+    dump += StringPrintf(INDENT4 "External Stylus Buttons Applied: 0x%08x\n",
                          mExternalStylusButtonsApplied);
     dump += INDENT3 "External Stylus State:\n";
     dumpStylusState(dump, mExternalStylusState);
@@ -972,7 +972,18 @@
             (rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;
 
     const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
-    const bool viewportChanged = mViewport != newViewport;
+    bool viewportChanged;
+    if (mParameters.enableForInactiveViewport) {
+        // When touch is enabled for an inactive viewport, ignore the
+        // viewport active status when checking whether the viewport has
+        // changed.
+        DisplayViewport tempViewport = mViewport;
+        tempViewport.isActive = newViewport.isActive;
+        viewportChanged = tempViewport != newViewport;
+    } else {
+        viewportChanged = mViewport != newViewport;
+    }
+
     bool skipViewportUpdate = false;
     if (viewportChanged) {
         const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
@@ -1998,12 +2009,12 @@
         PointerCoords& curOutCoords = outCoords[outIndex];
 
         if (curInProperties != curOutProperties) {
-            curOutProperties.copyFrom(curInProperties);
+            curOutProperties = curInProperties;
             changed = true;
         }
 
         if (curInCoords != curOutCoords) {
-            curOutCoords.copyFrom(curInCoords);
+            curOutCoords = curInCoords;
             changed = true;
         }
     }
@@ -2733,7 +2744,7 @@
                                        buttonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
-                                       /* videoFrames */ {}));
+                                       /*videoFrames=*/{}));
     }
 
     // Update state.
@@ -2745,10 +2756,9 @@
         for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
-            mPointerGesture.lastGestureProperties[index].copyFrom(
-                    mPointerGesture.currentGestureProperties[index]);
-            mPointerGesture.lastGestureCoords[index].copyFrom(
-                    mPointerGesture.currentGestureCoords[index]);
+            mPointerGesture.lastGestureProperties[index] =
+                    mPointerGesture.currentGestureProperties[index];
+            mPointerGesture.lastGestureCoords[index] = mPointerGesture.currentGestureCoords[index];
             mPointerGesture.lastGestureIdToIndex[id] = index;
         }
     }
@@ -3532,8 +3542,7 @@
             std::tie(x, y) = mPointerController->getPosition();
         }
 
-        mPointerSimple.currentCoords.copyFrom(
-                mCurrentCookedState.cookedPointerData.pointerCoords[index]);
+        mPointerSimple.currentCoords = mCurrentCookedState.cookedPointerData.pointerCoords[index];
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerSimple.currentProperties.id = 0;
@@ -3571,8 +3580,8 @@
 
         const auto [x, y] = mPointerController->getPosition();
         const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
-        mPointerSimple.currentCoords.copyFrom(
-                mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
+        mPointerSimple.currentCoords =
+                mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex];
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
@@ -3632,7 +3641,7 @@
                                        mOrientedXPrecision, mOrientedYPrecision,
                                        mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
                                        mPointerSimple.downTime,
-                                       /* videoFrames */ {}));
+                                       /*videoFrames=*/{}));
     }
 
     if (mPointerSimple.hovering && !hovering) {
@@ -3647,7 +3656,7 @@
                                  &mPointerSimple.lastCoords, mOrientedXPrecision,
                                  mOrientedYPrecision, mPointerSimple.lastCursorX,
                                  mPointerSimple.lastCursorY, mPointerSimple.downTime,
-                                 /* videoFrames */ {}));
+                                 /*videoFrames=*/{}));
     }
 
     if (down) {
@@ -3664,7 +3673,7 @@
                                            &mPointerSimple.currentProperties,
                                            &mPointerSimple.currentCoords, mOrientedXPrecision,
                                            mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
-                                           mPointerSimple.downTime, /* videoFrames */ {}));
+                                           mPointerSimple.downTime, /*videoFrames=*/{}));
         }
 
         // Send move.
@@ -3675,7 +3684,7 @@
                                        &mPointerSimple.currentProperties,
                                        &mPointerSimple.currentCoords, mOrientedXPrecision,
                                        mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
-                                       mPointerSimple.downTime, /* videoFrames */ {}));
+                                       mPointerSimple.downTime, /*videoFrames=*/{}));
     }
 
     if (hovering) {
@@ -3691,7 +3700,7 @@
                                            &mPointerSimple.currentProperties,
                                            &mPointerSimple.currentCoords, mOrientedXPrecision,
                                            mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
-                                           mPointerSimple.downTime, /* videoFrames */ {}));
+                                           mPointerSimple.downTime, /*videoFrames=*/{}));
         }
 
         // Send hover move.
@@ -3702,7 +3711,7 @@
                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                                  mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
-                                 cursorPosition.y, mPointerSimple.downTime, /* videoFrames */ {}));
+                                 cursorPosition.y, mPointerSimple.downTime, /*videoFrames=*/{}));
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3712,8 +3721,7 @@
         mWheelXVelocityControl.move(when, &hscroll, nullptr);
 
         // Send scroll.
-        PointerCoords pointerCoords;
-        pointerCoords.copyFrom(mPointerSimple.currentCoords);
+        PointerCoords pointerCoords = mPointerSimple.currentCoords;
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
@@ -3724,13 +3732,13 @@
                                        &mPointerSimple.currentProperties, &pointerCoords,
                                        mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
                                        cursorPosition.y, mPointerSimple.downTime,
-                                       /* videoFrames */ {}));
+                                       /*videoFrames=*/{}));
     }
 
     // Save state.
     if (down || hovering) {
-        mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
-        mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+        mPointerSimple.lastCoords = mPointerSimple.currentCoords;
+        mPointerSimple.lastProperties = mPointerSimple.currentProperties;
         mPointerSimple.displayId = displayId;
         mPointerSimple.source = mSource;
         mPointerSimple.lastCursorX = cursorPosition.x;
@@ -3755,7 +3763,7 @@
                                        mOrientedXPrecision, mOrientedYPrecision,
                                        mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
                                        mPointerSimple.downTime,
-                                       /* videoFrames */ {}));
+                                       /*videoFrames=*/{}));
         if (mPointerController != nullptr) {
             mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         }
@@ -3784,8 +3792,8 @@
     while (!idBits.isEmpty()) {
         uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = idToIndex[id];
-        pointerProperties[pointerCount].copyFrom(properties[index]);
-        pointerCoords[pointerCount].copyFrom(coords[index]);
+        pointerProperties[pointerCount] = properties[index];
+        pointerCoords[pointerCount] = coords[index];
 
         if (changedId >= 0 && id == uint32_t(changedId)) {
             action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index d8b59ca..c5dfb00 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -195,9 +195,9 @@
     enum class DeviceMode {
         DISABLED,   // input is disabled
         DIRECT,     // direct mapping (touchscreen)
-        UNSCALED,   // unscaled mapping (touchpad)
+        UNSCALED,   // unscaled mapping (e.g. captured touchpad)
         NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
-        POINTER,    // pointer mapping (pointer)
+        POINTER,    // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
 
         ftl_last = POINTER
     };
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 300bb85..38640b3 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -59,6 +59,7 @@
         "NotifyArgs_test.cpp",
         "PreferStylusOverTouch_test.cpp",
         "PropertyProvider_test.cpp",
+        "SlopController_test.cpp",
         "SyncQueue_test.cpp",
         "TestInputListener.cpp",
         "TouchpadInputMapper_test.cpp",
@@ -74,25 +75,9 @@
     target: {
         android: {
             shared_libs: [
-                "libinput",
                 "libvintf",
             ],
         },
-        host: {
-            sanitize: {
-                address: true,
-            },
-            include_dirs: [
-                "bionic/libc/kernel/android/uapi/",
-                "bionic/libc/kernel/uapi",
-            ],
-            cflags: [
-                "-D__ANDROID_HOST__",
-            ],
-            static_libs: [
-                "libinput",
-            ],
-        },
     },
     sanitize: {
         hwaddress: true,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 51202b7..d367cd7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -206,11 +206,9 @@
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
-    InputDispatcherConfiguration mConfig;
-
     struct AnrResult {
         sp<IBinder> token{};
-        gui::Pid pid{-1};
+        gui::Pid pid{gui::Pid::INVALID};
     };
 
 public:
@@ -349,11 +347,6 @@
                    "signal";
     }
 
-    void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
-        mConfig.keyRepeatTimeout = timeout;
-        mConfig.keyRepeatDelay = delay;
-    }
-
     PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
         std::unique_lock lock(mLock);
         base::ScopedLockAssertion assumeLocked(mLock);
@@ -550,8 +543,6 @@
 
     void notifyVibratorState(int32_t deviceId, bool isOn) override {}
 
-    InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
     bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
         std::scoped_lock lock(mLock);
         switch (inputEvent.getType()) {
@@ -650,7 +641,7 @@
     void SetUp() override {
         mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
         mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
-        mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+        mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
         // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
     }
@@ -691,7 +682,7 @@
     // Rejects undefined key actions.
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
                      INVALID_HMAC,
-                     /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     /*action=*/-1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
                      ARBITRARY_TIME);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
@@ -727,11 +718,11 @@
     ui::Transform identityTransform;
     // Rejects undefined motion actions.
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
-                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+                     /*action=*/-1, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -743,7 +734,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -755,7 +746,7 @@
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -767,7 +758,7 @@
                      0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -779,7 +770,7 @@
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -791,7 +782,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 0, pointerProperties, pointerCoords);
+                     /*pointerCount=*/0, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -802,7 +793,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -815,7 +806,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -827,7 +818,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                     /*pointerCount=*/1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -841,7 +832,7 @@
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
                      ARBITRARY_TIME,
-                     /*pointerCount*/ 2, pointerProperties, pointerCoords);
+                     /*pointerCount=*/2, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
                                             0ms, 0))
@@ -1560,8 +1551,8 @@
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
     NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
-                       displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
-                       KEY_A, AMETA_NONE, currentTime);
+                       displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A,
+                       AMETA_NONE, currentTime);
 
     return args;
 }
@@ -1571,7 +1562,7 @@
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
     NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
-                       displayId, 0, action, /* flags */ 0, AKEYCODE_C, KEY_C, AMETA_META_ON,
+                       displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON,
                        currentTime);
 
     return args;
@@ -1582,7 +1573,7 @@
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
     NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
-                       displayId, 0, action, /* flags */ 0, AKEYCODE_ASSIST, KEY_ASSISTANT,
+                       displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT,
                        AMETA_NONE, currentTime);
 
     return args;
@@ -1612,12 +1603,12 @@
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion event.
     NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId,
-                          POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
-                          AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                          POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0,
+                          AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
                           AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
-                          pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+                          pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /*videoFrames=*/{});
 
     return args;
 }
@@ -1932,8 +1923,7 @@
                                                    AINPUT_SOURCE_TOUCHSCREEN)
                                         .displayId(ADISPLAY_ID_DEFAULT)
                                         .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
-                                        .pointer(PointerBuilder(/* id */ 1,
-                                                                ToolType::FINGER)
+                                        .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
                                                          .x(100)
                                                          .y(100))
                                         .build(),
@@ -3941,9 +3931,7 @@
 /**
  * Send a two-pointer gesture to a single window. The window's orientation changes in response to
  * the first pointer.
- * Ensure that the second pointer is not sent to the window.
- *
- * The subsequent gesture should be correctly delivered to the window.
+ * Ensure that the second pointer and the subsequent gesture is correctly delivered to the window.
  */
 TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -3973,8 +3961,6 @@
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDup}}});
 
-    window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
-
     mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                       .downTime(baseTime + 10)
                                       .eventTime(baseTime + 30)
@@ -3982,19 +3968,26 @@
                                       .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
                                       .build());
 
-    // Finish the gesture and start a new one. Ensure the new gesture is sent to the window
+    window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+    // Finish the gesture and start a new one. Ensure all events are sent to the window.
     mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
                                       .downTime(baseTime + 10)
                                       .eventTime(baseTime + 40)
                                       .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
                                       .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
                                       .build());
+
+    window->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
+
     mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
                                       .downTime(baseTime + 10)
                                       .eventTime(baseTime + 50)
                                       .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
                                       .build());
 
+    window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
     mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                       .downTime(baseTime + 60)
                                       .eventTime(baseTime + 60)
@@ -5705,10 +5698,9 @@
 
     virtual void SetUp() override {
         mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
-        mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
         mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
-        mDispatcher->requestRefreshConfiguration();
-        mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+        mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+        mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
         ASSERT_EQ(OK, mDispatcher->start());
 
         setUpWindow();
@@ -6099,36 +6091,36 @@
 // Test InputFilter for MotionEvent
 TEST_F(InputFilterTest, MotionEvent_InputFilter) {
     // Since the InputFilter is disabled by default, check if touch events aren't filtered.
-    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
-    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
 
     // Enable InputFilter
     mDispatcher->setInputFilterEnabled(true);
     // Test touch on both primary and second display, and check if both events are filtered.
-    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
-    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true);
 
     // Disable InputFilter
     mDispatcher->setInputFilterEnabled(false);
     // Test touch on both primary and second display, and check if both events aren't filtered.
-    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
-    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
 }
 
 // Test InputFilter for KeyEvent
 TEST_F(InputFilterTest, KeyEvent_InputFilter) {
     // Since the InputFilter is disabled by default, check if key event aren't filtered.
-    testNotifyKey(/*expectToBeFiltered*/ false);
+    testNotifyKey(/*expectToBeFiltered=*/false);
 
     // Enable InputFilter
     mDispatcher->setInputFilterEnabled(true);
     // Send a key event, and check if it is filtered.
-    testNotifyKey(/*expectToBeFiltered*/ true);
+    testNotifyKey(/*expectToBeFiltered=*/true);
 
     // Disable InputFilter
     mDispatcher->setInputFilterEnabled(false);
     // Send a key event, and check if it isn't filtered.
-    testNotifyKey(/*expectToBeFiltered*/ false);
+    testNotifyKey(/*expectToBeFiltered=*/false);
 }
 
 // Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
@@ -6151,8 +6143,8 @@
     mDispatcher->setInputFilterEnabled(true);
 
     // Ensure the correct transforms are used for the displays.
-    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
-    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true, firstDisplayTransform);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true, secondDisplayTransform);
 }
 
 class InputFilterInjectionPolicyTest : public InputDispatcherTest {
@@ -6191,7 +6183,7 @@
                 POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
         ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                   mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
-                                                InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+                                                InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
                                                 policyFlags | additionalPolicyFlags));
 
         InputEvent* received = mWindow->consume();
@@ -6221,12 +6213,12 @@
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                          AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
                          eventTime,
-                         /*pointerCount*/ 1, pointerProperties, pointerCoords);
+                         /*pointerCount=*/1, pointerProperties, pointerCoords);
 
         const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
         ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                   mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
-                                                InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+                                                InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
                                                 policyFlags | additionalPolicyFlags));
 
         InputEvent* received = mWindow->consume();
@@ -6614,11 +6606,11 @@
         InputDispatcherTest::SetUp();
 
         mApplication = std::make_shared<FakeApplicationHandle>();
-        mApplication->setDispatchingTimeout(20ms);
+        mApplication->setDispatchingTimeout(100ms);
         mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "TestWindow",
                                              ADISPLAY_ID_DEFAULT);
         mWindow->setFrame(Rect(0, 0, 30, 30));
-        mWindow->setDispatchingTimeout(30ms);
+        mWindow->setDispatchingTimeout(100ms);
         mWindow->setFocusable(true);
 
         // Set focused application.
@@ -6684,7 +6676,7 @@
 
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+                      InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
                       /*allowKeyRepeat=*/false);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
     // Key will not go to window because we have no focused window.
@@ -6748,7 +6740,7 @@
     // injection times out (instead of failing).
     const InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
     mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -6770,7 +6762,7 @@
 
     // Define a valid key down event that is stale (too old).
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A,
                      AMETA_NONE, /*repeatCount=*/1, eventTime, eventTime);
 
     const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
@@ -6799,7 +6791,7 @@
     // injection times out (instead of failing).
     const InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration appTimeout =
             mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6823,7 +6815,7 @@
     // Once a focused event arrives, we get an ANR for this application
     const InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
 
     const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -7039,7 +7031,7 @@
 
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     // Key will not be sent to the window, yet, because the window is still processing events
     // and the key remains pending, waiting for the touch events to be processed
@@ -7144,7 +7136,7 @@
         InputDispatcherTest::SetUp();
 
         mApplication = std::make_shared<FakeApplicationHandle>();
-        mApplication->setDispatchingTimeout(10ms);
+        mApplication->setDispatchingTimeout(100ms);
         mUnfocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Unfocused",
                                                       ADISPLAY_ID_DEFAULT);
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
@@ -7153,7 +7145,7 @@
 
         mFocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Focused",
                                                     ADISPLAY_ID_DEFAULT);
-        mFocusedWindow->setDispatchingTimeout(30ms);
+        mFocusedWindow->setDispatchingTimeout(100ms);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
 
         // Set focused application.
@@ -7239,20 +7231,21 @@
 // But we should receive ANR for both.
 TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
     // Set the timeout for unfocused window to match the focused window
-    mUnfocusedWindow->setDispatchingTimeout(10ms);
+    mUnfocusedWindow->setDispatchingTimeout(
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
 
     tapOnFocusedWindow();
     // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
-    sp<IBinder> anrConnectionToken1, anrConnectionToken2;
-    ASSERT_NO_FATAL_FAILURE(anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms));
-    ASSERT_NO_FATAL_FAILURE(anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms));
-
     // We don't know which window will ANR first. But both of them should happen eventually.
-    ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
-                mFocusedWindow->getToken() == anrConnectionToken2);
-    ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
-                mUnfocusedWindow->getToken() == anrConnectionToken2);
+    std::array<sp<IBinder>, 2> anrConnectionTokens = {mFakePolicy->getUnresponsiveWindowToken(
+                                                              mFocusedWindow->getDispatchingTimeout(
+                                                                      DISPATCHING_TIMEOUT)),
+                                                      mFakePolicy->getUnresponsiveWindowToken(0ms)};
+
+    ASSERT_THAT(anrConnectionTokens,
+                ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+                                                testing::Eq(mUnfocusedWindow->getToken())));
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -7261,15 +7254,13 @@
     mFocusedWindow->consumeMotionUp();
     mUnfocusedWindow->consumeMotionOutside();
 
-    sp<IBinder> responsiveToken1, responsiveToken2;
-    ASSERT_NO_FATAL_FAILURE(responsiveToken1 = mFakePolicy->getResponsiveWindowToken());
-    ASSERT_NO_FATAL_FAILURE(responsiveToken2 = mFakePolicy->getResponsiveWindowToken());
+    std::array<sp<IBinder>, 2> responsiveTokens = {mFakePolicy->getResponsiveWindowToken(),
+                                                   mFakePolicy->getResponsiveWindowToken()};
 
     // Both applications should be marked as responsive, in any order
-    ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
-                mFocusedWindow->getToken() == responsiveToken2);
-    ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
-                mUnfocusedWindow->getToken() == responsiveToken2);
+    ASSERT_THAT(responsiveTokens,
+                ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+                                                testing::Eq(mUnfocusedWindow->getToken())));
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -7369,7 +7360,7 @@
 
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms);
+                      InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
     // Key will not be sent to the window, yet, because the window is still processing events
     // and the key remains pending, waiting for the touch events to be processed
@@ -7457,7 +7448,7 @@
 TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
     std::shared_ptr<FakeApplicationHandle> focusedApplication =
             std::make_shared<FakeApplicationHandle>();
-    focusedApplication->setDispatchingTimeout(60ms);
+    focusedApplication->setDispatchingTimeout(100ms);
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
     // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
     mFocusedWindow->setFocusable(false);
@@ -7470,7 +7461,7 @@
     // Key will not be sent anywhere because we have no focused window. It will remain pending.
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+                      InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
                       /*allowKeyRepeat=*/false);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
 
@@ -7481,7 +7472,7 @@
     // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
     // For this test, it means that the key would get delivered to the window once it becomes
     // focused.
-    std::this_thread::sleep_for(10ms);
+    std::this_thread::sleep_for(50ms);
 
     // Touch unfocused window. This should force the pending key to get dropped.
     mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -9442,8 +9433,8 @@
 
     // Spy window pilfers the pointers.
     EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
-    window->consumeMotionPointerUp(/* idx */ 2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
-    window->consumeMotionPointerUp(/* idx */ 1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+    window->consumeMotionPointerUp(/*idx=*/2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+    window->consumeMotionPointerUp(/*idx=*/1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
 
     spy->assertNoEvents();
     window->assertNoEvents();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d1c3f7d..e751c89 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -161,6 +161,7 @@
     // fake mapping which would normally come from keyCharacterMap
     std::unordered_map<int32_t, int32_t> mKeyCodeMapping;
     std::vector<int32_t> mSupportedKeyCodes;
+    std::list<NotifyArgs> mProcessResult;
 
     std::mutex mLock;
     std::condition_variable mStateChangedCondition;
@@ -191,6 +192,14 @@
         mMetaState = metaState;
     }
 
+    // Sets the return value for the `process` call.
+    void setProcessResult(std::list<NotifyArgs> notifyArgs) {
+        mProcessResult.clear();
+        for (auto notifyArg : notifyArgs) {
+            mProcessResult.push_back(notifyArg);
+        }
+    }
+
     void assertConfigureWasCalled() {
         std::unique_lock<std::mutex> lock(mLock);
         base::ScopedLockAssertion assumeLocked(mLock);
@@ -291,7 +300,7 @@
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
-        return {};
+        return mProcessResult;
     }
 
     int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -2475,6 +2484,73 @@
     ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
 }
 
+TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) {
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1");
+    FakeInputMapper& mapper =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                AINPUT_SOURCE_KEYBOARD);
+    NotifyMotionArgs args1;
+    NotifySwitchArgs args2;
+    NotifyKeyArgs args3;
+    mapper.setProcessResult({args1, args2, args3});
+
+    InputReaderConfiguration config;
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+    RawEvent event;
+    event.deviceId = EVENTHUB_ID;
+    std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+    for (auto& arg : notifyArgs) {
+        if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+            ASSERT_EQ(POLICY_FLAG_WAKE, notifyMotionArgs->policyFlags);
+        } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+            ASSERT_EQ(POLICY_FLAG_WAKE, notifySwitchArgs->policyFlags);
+        } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+            ASSERT_EQ(POLICY_FLAG_WAKE, notifyKeyArgs->policyFlags);
+        }
+    }
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotAddWakeFlagToProcessNotifyArgs) {
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+    FakeInputMapper& mapper =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                AINPUT_SOURCE_KEYBOARD);
+    NotifyMotionArgs args;
+    mapper.setProcessResult({args});
+
+    InputReaderConfiguration config;
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+    RawEvent event;
+    event.deviceId = EVENTHUB_ID;
+    std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+    // POLICY_FLAG_WAKE is not added to the NotifyArgs.
+    ASSERT_EQ(0u, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotRemoveExistingWakeFlagFromProcessNotifyArgs) {
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+    FakeInputMapper& mapper =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                AINPUT_SOURCE_KEYBOARD);
+    NotifyMotionArgs args;
+    args.policyFlags = POLICY_FLAG_WAKE;
+    mapper.setProcessResult({args});
+
+    InputReaderConfiguration config;
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+    RawEvent event;
+    event.deviceId = EVENTHUB_ID;
+    std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+    // The POLICY_FLAG_WAKE is preserved, despite the device being a non-wake device.
+    ASSERT_EQ(POLICY_FLAG_WAKE, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
 // A single input device is associated with a specific display. Check that:
 // 1. Device is disabled if the viewport corresponding to the associated display is not found
 // 2. Device is disabled when setEnabled API is called
@@ -3727,6 +3803,19 @@
     ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
 }
 
+TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
+    KeyboardInputMapper& mapper =
+            constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    NotifyKeyArgs args;
+
+    // Key down
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -9373,6 +9462,11 @@
     EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
 }
 
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is false, touches prior to the transition
+ * should be cancelled.
+ */
 TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.enableForInactiveViewport", "0");
@@ -9424,6 +9518,60 @@
     EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
 }
 
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is true, touches prior to the transition
+ * should not be cancelled.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    addConfigurationProperty("touch.enableForInactiveViewport", "1");
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+    std::optional<DisplayViewport> optionalDisplayViewport =
+            mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+    ASSERT_TRUE(optionalDisplayViewport.has_value());
+    DisplayViewport displayViewport = *optionalDisplayViewport;
+
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
+
+    // Finger down
+    int32_t x = 100, y = 100;
+    processPosition(mapper, x, y);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    // Deactivate display viewport
+    displayViewport.isActive = false;
+    ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    // The ongoing touch should not be canceled
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Finger move is not ignored
+    x += 10, y += 10;
+    processPosition(mapper, x, y);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+    // Reactivate display viewport
+    displayViewport.isActive = true;
+    ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    // Finger move continues and does not start new gesture
+    x += 10, y += 10;
+    processPosition(mapper, x, y);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
     // Setup the first touch screen device.
     prepareAxes(POSITION | ID | SLOT);
diff --git a/services/inputflinger/tests/SlopController_test.cpp b/services/inputflinger/tests/SlopController_test.cpp
new file mode 100644
index 0000000..f524acd
--- /dev/null
+++ b/services/inputflinger/tests/SlopController_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../reader/mapper/SlopController.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- SlopControllerTest ---
+
+TEST(SlopControllerTest, PositiveValues) {
+    SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(0, controller.consumeEvent(1000, 1));
+    ASSERT_EQ(0, controller.consumeEvent(1003, 3));
+    ASSERT_EQ(2, controller.consumeEvent(1005, 3));
+    ASSERT_EQ(4, controller.consumeEvent(1009, 4));
+
+    SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(0, controller2.consumeEvent(1000, 5));
+    ASSERT_EQ(3, controller2.consumeEvent(1003, 3));
+    ASSERT_EQ(4, controller2.consumeEvent(1005, 4));
+}
+
+TEST(SlopControllerTest, NegativeValues) {
+    SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(0, controller.consumeEvent(1000, -1));
+    ASSERT_EQ(0, controller.consumeEvent(1003, -3));
+    ASSERT_EQ(-2, controller.consumeEvent(1005, -3));
+    ASSERT_EQ(-4, controller.consumeEvent(1009, -4));
+
+    SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(0, controller2.consumeEvent(1000, -5));
+    ASSERT_EQ(-3, controller2.consumeEvent(1003, -3));
+    ASSERT_EQ(-4, controller2.consumeEvent(1005, -4));
+}
+
+TEST(SlopControllerTest, ZeroDoesNotResetSlop) {
+    SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+    ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+    ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+}
+
+TEST(SlopControllerTest, SignChange_ResetsSlop) {
+    SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(0, controller.consumeEvent(1000, 2));
+    ASSERT_EQ(0, controller.consumeEvent(1001, -4));
+    ASSERT_EQ(0, controller.consumeEvent(1002, 3));
+    ASSERT_EQ(0, controller.consumeEvent(1003, -2));
+
+    ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+    ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+    ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+
+    ASSERT_EQ(0, controller.consumeEvent(1010, -4));
+    ASSERT_EQ(-1, controller.consumeEvent(1011, -2));
+
+    ASSERT_EQ(0, controller.consumeEvent(1015, 5));
+    ASSERT_EQ(2, controller.consumeEvent(1016, 2));
+
+    ASSERT_EQ(0, controller.consumeEvent(1017, -5));
+    ASSERT_EQ(-2, controller.consumeEvent(1018, -2));
+}
+
+TEST(SlopControllerTest, OldAge_ResetsSlop) {
+    SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+    ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+    ASSERT_EQ(0, controller.consumeEvent(1108, 2)); // age exceeds slop duration
+
+    ASSERT_EQ(1, controller.consumeEvent(1110, 4));
+    ASSERT_EQ(0, controller.consumeEvent(1210, 2)); // age equals slop duration
+
+    ASSERT_EQ(0, controller.consumeEvent(1215, -3));
+    ASSERT_EQ(-2, controller.consumeEvent(1216, -4));
+    ASSERT_EQ(-5, controller.consumeEvent(1315, -5));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index da0815f..7cfcf71 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -88,14 +88,13 @@
     }
 
     // Define a valid motion event.
-    NotifyMotionArgs args(/* id */ 0, eventTime, /*readTime=*/0, DEVICE_ID,
-                          AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, POLICY_FLAG_PASS_TO_USER,
-                          action, /* actionButton */ 0,
-                          /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
-                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
-                          pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+    NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+                          /*displayId=*/0, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0,
+                          /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+                          AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+                          pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {});
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /*videoFrames=*/{});
 
     return args;
 }
@@ -104,15 +103,15 @@
     InputDeviceIdentifier identifier;
 
     auto info = InputDeviceInfo();
-    info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias",
-                    /*isExternal*/ false, /*hasMic*/ false, ADISPLAY_ID_NONE);
+    info.initialize(DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
+                    /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
     info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
-    info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0,
-                        /*fuzz*/ 0, X_RESOLUTION);
-    info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0,
-                        /*fuzz*/ 0, Y_RESOLUTION);
+    info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat=*/0,
+                        /*fuzz=*/0, X_RESOLUTION);
+    info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat=*/0,
+                        /*fuzz=*/0, Y_RESOLUTION);
     info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255,
-                        /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION);
+                        /*flat=*/0, /*fuzz=*/0, MAJOR_RESOLUTION);
 
     return info;
 }
@@ -152,7 +151,7 @@
 }
 
 TEST(RemovePointerIdsTest, RemoveOnePointer) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
                                                AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
 
     NotifyMotionArgs pointer1Only = removePointerIds(args, {0});
@@ -167,7 +166,7 @@
  */
 TEST(RemovePointerIdsTest, RemoveTwoPointers) {
     NotifyMotionArgs args =
-            generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE,
+            generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, AMOTION_EVENT_ACTION_MOVE,
                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
 
     NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2});
@@ -179,7 +178,7 @@
  * pointer during a POINTER_DOWN event.
  */
 TEST(RemovePointerIdsTest, ActionPointerDown) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
 
     NotifyMotionArgs pointers0And2 = removePointerIds(args, {1});
@@ -193,7 +192,7 @@
  * Remove all pointers during a MOVE event.
  */
 TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
                                                AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
 
     NotifyMotionArgs noPointers = removePointerIds(args, {0, 1});
@@ -205,7 +204,7 @@
  * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event.
  */
 TEST(RemovePointerIdsTest, PointerDownBecomesDown) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
 
     NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2});
@@ -220,11 +219,11 @@
  * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event.
  */
 TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{1});
     ASSERT_TRUE(result.empty());
 }
 
@@ -232,11 +231,11 @@
  * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped
  */
 TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
-                                     /*newSuppressedPointerIds*/ {1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+                                     /*newSuppressedPointerIds=*/{1});
     ASSERT_TRUE(result.empty());
 }
 
@@ -244,11 +243,11 @@
  * If a pointer is already suppressed, it should be removed from a MOVE event.
  */
 TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
-                                     /*newSuppressedPointerIds*/ {1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+                                     /*newSuppressedPointerIds=*/{1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
 }
@@ -259,11 +258,11 @@
  * 2) A MOVE event without this pointer
  */
 TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{1});
     ASSERT_EQ(2u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -275,10 +274,10 @@
  * should be canceled with ACTION_CANCEL.
  */
 TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}});
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {0});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -289,11 +288,11 @@
  * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional.
  */
 TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -304,11 +303,11 @@
  * errors with handling pointer index inside the action.
  */
 TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_0_UP,
                                                {{1, 2, 3}, {4, 5, 6}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {0});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -320,10 +319,10 @@
  */
 TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) {
     NotifyMotionArgs args =
-            generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}});
+            generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}, {4, 5, 6}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
-                                     /*newSuppressedPointerIds*/ {0, 1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+                                     /*newSuppressedPointerIds=*/{0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -335,11 +334,11 @@
  * would undo the entire gesture.
  */
 TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
                                                {{1, 2, 3}, {4, 5, 6}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
-                                     /*newSuppressedPointerIds*/ {0, 1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+                                     /*newSuppressedPointerIds=*/{0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
     ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -350,11 +349,11 @@
  * this should become a regular DOWN event because it's the only pointer that will be valid now.
  */
 TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN,
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_2_DOWN,
                                                {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
     std::vector<NotifyMotionArgs> result =
-            cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1},
-                                     /*newSuppressedPointerIds*/ {0, 1});
+            cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{0, 1},
+                                     /*newSuppressedPointerIds=*/{0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], DOWN, {{2, {7, 8, 9}}});
     ASSERT_EQ(0, result[0].flags);
@@ -365,7 +364,7 @@
  * struct is populated as expected.
  */
 TEST(GetTouchesTest, ConvertDownEvent) {
-    NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}});
+    NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
     AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo();
     SlotState slotState;
     SlotState oldSlotState = slotState;
@@ -1024,7 +1023,7 @@
     mPalmRejector->processMotion(
             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+            generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
     // Suppress both pointers!!
     suppressPointerAtPosition(1414, 702);
@@ -1058,13 +1057,13 @@
     mPalmRejector->processMotion(
             generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
     mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+            generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
 
     // Suppress second pointer (pointer 1)
     suppressPointerAtPosition(1060, 700);
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, MOVE,
+            generateMotionArgs(downTime, /*eventTime=*/1, MOVE,
                                {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
     ASSERT_EQ(2u, argsList.size());
     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
@@ -1076,20 +1075,20 @@
     // A new pointer goes down and gets suppressed right away. It should just be dropped
     suppressPointerAtPosition(1001, 601);
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN,
+            generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_DOWN,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
 
     ASSERT_EQ(0u, argsList.size());
     // Likewise, pointer that's already canceled should be ignored
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP,
+            generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_UP,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
     ASSERT_EQ(0u, argsList.size());
 
     // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier.
     suppressPointerAtPosition(1417, 685);
     argsList = mPalmRejector->processMotion(
-            generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP,
+            generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_UP,
                                {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
     ASSERT_EQ(1u, argsList.size());
     ASSERT_EQ(CANCEL, argsList[0].action);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 47b0824..d7980f5 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -100,6 +100,22 @@
 }
 
 cc_fuzz {
+    name: "inputflinger_touchpad_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "TouchpadInputFuzzer.cpp",
+    ],
+    static_libs: [
+        "libchrome-gestures",
+    ],
+    header_libs: [
+        "libchrome-gestures_headers",
+    ],
+}
+
+cc_fuzz {
     name: "inputflinger_input_reader_fuzzer",
     defaults: [
         "inputflinger_fuzz_defaults",
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 8098ef2..e93b592 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -16,6 +16,8 @@
 
 #include <CursorInputMapper.h>
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
 
 namespace android {
 
@@ -38,7 +40,7 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    auto policyConfig = fuzzer.getPolicyConfig();
+    InputReaderConfiguration policyConfig;
     CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(policyConfig);
 
     // Loop through mapper operations until randomness is exhausted.
@@ -65,22 +67,11 @@
                     mapper.populateDeviceInfo(info);
                 },
                 [&]() -> void {
-                    int32_t type, code;
-                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-
                     // Need to reconfigure with 0 or you risk a NPE.
                     std::list<NotifyArgs> unused =
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
                                                InputReaderConfiguration::Change(0));
-                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<int32_t>(),
-                                      type,
-                                      code,
-                                      fdp->ConsumeIntegral<int32_t>()};
+                    RawEvent rawEvent = getFuzzedRawEvent(*fdp);
                     unused += mapper.process(&rawEvent);
                 },
                 [&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index b992928..ade5328 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -25,11 +25,9 @@
 
 class FuzzContainer {
     std::shared_ptr<FuzzEventHub> mFuzzEventHub;
-    sp<FuzzInputReaderPolicy> mFuzzPolicy;
     FuzzInputListener mFuzzListener;
     std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
     std::unique_ptr<InputDevice> mFuzzDevice;
-    InputReaderConfiguration mPolicyConfig;
     std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
 
 public:
@@ -38,12 +36,12 @@
         std::string deviceName = mFdp->ConsumeRandomLengthString(16);
         std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
         int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5);
-        int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5);
+        int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from=*/0, /*to=*/5);
 
         // Create mocked objects.
         mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
-        mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
-        mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
+        sp<FuzzInputReaderPolicy> policy = sp<FuzzInputReaderPolicy>::make(mFdp);
+        mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, policy,
                                                                 mFuzzListener, mFdp);
 
         InputDeviceIdentifier identifier;
@@ -51,7 +49,6 @@
         identifier.location = deviceLocation;
         mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
                                                     identifier);
-        mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
     }
 
     ~FuzzContainer() {}
@@ -59,7 +56,7 @@
     void configureDevice() {
         nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
         std::list<NotifyArgs> out;
-        out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
+        out += mFuzzDevice->configure(arbitraryTime, /*readerConfig=*/{}, /*changes=*/{});
         out += mFuzzDevice->reset(arbitraryTime);
         for (const NotifyArgs& args : out) {
             mFuzzListener.notify(args);
@@ -71,7 +68,9 @@
         configureDevice();
     }
 
-    InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
+    void setAbsoluteAxisInfo(int axis, const RawAbsoluteAxisInfo& axisInfo) {
+        mFuzzEventHub->setAbsoluteAxisInfo(mFuzzDevice->getId(), axis, axisInfo);
+    }
 
     template <class T, typename... Args>
     T& getMapper(Args... args) {
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 616e870..54977df 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -15,7 +15,9 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
 #include <KeyboardInputMapper.h>
+#include <MapperHelpers.h>
 
 namespace android {
 
@@ -44,9 +46,9 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    auto policyConfig = fuzzer.getPolicyConfig();
     KeyboardInputMapper& mapper =
-            fuzzer.getMapper<KeyboardInputMapper>(policyConfig, fdp->ConsumeIntegral<uint32_t>(),
+            fuzzer.getMapper<KeyboardInputMapper>(InputReaderConfiguration{},
+                                                  fdp->ConsumeIntegral<uint32_t>(),
                                                   fdp->ConsumeIntegral<int32_t>());
 
     // Loop through mapper operations until randomness is exhausted.
@@ -64,7 +66,7 @@
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
                     std::list<NotifyArgs> unused =
-                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), /*readerConfig=*/{},
                                                InputReaderConfiguration::Change(
                                                        fdp->ConsumeIntegral<uint32_t>()));
                 },
@@ -72,17 +74,7 @@
                     std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
                 },
                 [&]() -> void {
-                    int32_t type, code;
-                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<int32_t>(),
-                                      type,
-                                      code,
-                                      fdp->ConsumeIntegral<int32_t>()};
+                    RawEvent rawEvent = getFuzzedRawEvent(*fdp);
                     std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 1e44e0f..5039d1a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -15,6 +15,9 @@
  */
 #pragma once
 
+#include <map>
+
+#include <EventHub.h>
 #include <InputDevice.h>
 #include <InputMapper.h>
 #include <InputReader.h>
@@ -22,7 +25,6 @@
 
 constexpr size_t kValidTypes[] = {EV_SW,
                                   EV_SYN,
-                                  SYN_REPORT,
                                   EV_ABS,
                                   EV_KEY,
                                   EV_MSC,
@@ -46,7 +48,6 @@
         ABS_MT_PRESSURE,
         ABS_MT_DISTANCE,
         ABS_MT_TOOL_TYPE,
-        SYN_MT_REPORT,
         MSC_SCAN,
         REL_X,
         REL_Y,
@@ -74,10 +75,27 @@
     return static_cast<ToolType>(toolType);
 }
 
+template <class Fdp>
+RawEvent getFuzzedRawEvent(Fdp& fdp) {
+    const int32_t type = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidTypes)
+                                           : fdp.template ConsumeIntegral<int32_t>();
+    const int32_t code = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidCodes)
+                                           : fdp.template ConsumeIntegral<int32_t>();
+    return RawEvent{
+            .when = fdp.template ConsumeIntegral<nsecs_t>(),
+            .readTime = fdp.template ConsumeIntegral<nsecs_t>(),
+            .deviceId = fdp.template ConsumeIntegral<int32_t>(),
+            .type = type,
+            .code = code,
+            .value = fdp.template ConsumeIntegral<int32_t>(),
+    };
+}
+
 class FuzzEventHub : public EventHubInterface {
     InputDeviceIdentifier mIdentifier;
     std::vector<TouchVideoFrame> mVideoFrames;
     PropertyMap mFuzzConfig;
+    std::map<int32_t /* deviceId */, std::map<int /* axis */, RawAbsoluteAxisInfo>> mAxes;
     std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
 
 public:
@@ -97,8 +115,18 @@
     std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override {
         return mFuzzConfig;
     }
+    void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) {
+        mAxes[deviceId][axis] = axisInfo;
+    }
     status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
                                  RawAbsoluteAxisInfo* outAxisInfo) const override {
+        if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) {
+            const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second;
+            if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) {
+                *outAxisInfo = axisInfoIt->second;
+                return OK;
+            }
+        }
         return mFdp->ConsumeIntegral<status_t>();
     }
     bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
@@ -118,18 +146,7 @@
         std::vector<RawEvent> events;
         const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
         for (size_t i = 0; i < count; ++i) {
-            int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
-                                               : mFdp->ConsumeIntegral<int32_t>();
-            int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
-                                               : mFdp->ConsumeIntegral<int32_t>();
-            events.push_back({
-                    .when = mFdp->ConsumeIntegral<nsecs_t>(),
-                    .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
-                    .deviceId = mFdp->ConsumeIntegral<int32_t>(),
-                    .type = type,
-                    .code = code,
-                    .value = mFdp->ConsumeIntegral<int32_t>(),
-            });
+            events.push_back(getFuzzedRawEvent(*mFdp));
         }
         return events;
     }
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 212462d..569767f 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
 #include <MultiTouchInputMapper.h>
 
 namespace android {
@@ -61,7 +63,7 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    auto policyConfig = fuzzer.getPolicyConfig();
+    InputReaderConfiguration policyConfig;
     MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(policyConfig);
 
     // Loop through mapper operations until randomness is exhausted.
@@ -87,16 +89,7 @@
                     std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
                 },
                 [&]() -> void {
-                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                                      : fdp->ConsumeIntegral<int32_t>();
-                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                                      : fdp->ConsumeIntegral<int32_t>();
-                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<int32_t>(),
-                                      type,
-                                      code,
-                                      fdp->ConsumeIntegral<int32_t>()};
+                    RawEvent rawEvent = getFuzzedRawEvent(*fdp);
                     std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index 590207e..80eebd5 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
 #include <SwitchInputMapper.h>
 
 namespace android {
@@ -24,8 +26,7 @@
             std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
     FuzzContainer fuzzer(fdp);
 
-    auto policyConfig = fuzzer.getPolicyConfig();
-    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(policyConfig);
+    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(InputReaderConfiguration{});
 
     // Loop through mapper operations until randomness is exhausted.
     while (fdp->remaining_bytes() > 0) {
@@ -36,16 +37,7 @@
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
-                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                                      : fdp->ConsumeIntegral<int32_t>();
-                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                                      : fdp->ConsumeIntegral<int32_t>();
-                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<nsecs_t>(),
-                                      fdp->ConsumeIntegral<int32_t>(),
-                                      type,
-                                      code,
-                                      fdp->ConsumeIntegral<int32_t>()};
+                    RawEvent rawEvent = getFuzzedRawEvent(*fdp);
                     std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
new file mode 100644
index 0000000..796178a
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+#include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
+#include <TouchpadInputMapper.h>
+
+namespace android {
+
+namespace {
+
+void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer, int axis) {
+    if (fdp.ConsumeBool()) {
+        fuzzer.setAbsoluteAxisInfo(axis,
+                                   RawAbsoluteAxisInfo{
+                                           .valid = fdp.ConsumeBool(),
+                                           .minValue = fdp.ConsumeIntegral<int32_t>(),
+                                           .maxValue = fdp.ConsumeIntegral<int32_t>(),
+                                           .flat = fdp.ConsumeIntegral<int32_t>(),
+                                           .fuzz = fdp.ConsumeIntegral<int32_t>(),
+                                           .resolution = fdp.ConsumeIntegral<int32_t>(),
+                                   });
+    }
+}
+
+void setAxisInfos(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer) {
+    setAxisInfo(fdp, fuzzer, ABS_MT_SLOT);
+    setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_X);
+    setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_Y);
+    setAxisInfo(fdp, fuzzer, ABS_MT_PRESSURE);
+    setAxisInfo(fdp, fuzzer, ABS_MT_ORIENTATION);
+    setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MAJOR);
+    setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MINOR);
+    setAxisInfo(fdp, fuzzer, ABS_MT_WIDTH_MAJOR);
+    setAxisInfo(fdp, fuzzer, ABS_MT_WIDTH_MINOR);
+}
+
+const std::vector<std::string> boolPropertiesToFuzz = {
+        "gestureProp.Compute_Surface_Area_from_Pressure",
+        "gestureProp.Drumroll_Suppression_Enable",
+        "gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls",
+        "gestureProp.Stationary_Wiggle_Filter_Enabled",
+};
+const std::vector<std::string> doublePropertiesToFuzz = {
+        "gestureProp.Fake_Timestamp_Delta",
+        "gestureProp.Finger_Moving_Energy",
+        "gestureProp.Finger_Moving_Hysteresis",
+        "gestureProp.IIR_a1",
+        "gestureProp.IIR_a2",
+        "gestureProp.IIR_b0",
+        "gestureProp.IIR_b1",
+        "gestureProp.IIR_b2",
+        "gestureProp.IIR_b3",
+        "gestureProp.Max_Allowed_Pressure_Change_Per_Sec",
+        "gestureProp.Max_Hysteresis_Pressure_Per_Sec",
+        "gestureProp.Max_Stationary_Move_Speed",
+        "gestureProp.Max_Stationary_Move_Speed_Hysteresis",
+        "gestureProp.Max_Stationary_Move_Suppress_Distance",
+        "gestureProp.Multiple_Palm_Width",
+        "gestureProp.Palm_Edge_Zone_Width",
+        "gestureProp.Palm_Eval_Timeout",
+        "gestureProp.Palm_Pressure",
+        "gestureProp.Palm_Width",
+        "gestureProp.Pressure_Calibration_Offset",
+        "gestureProp.Pressure_Calibration_Slope",
+        "gestureProp.Tap_Exclusion_Border_Width",
+        "gestureProp.Touchpad_Device_Output_Bias_on_X-Axis",
+        "gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis",
+        "gestureProp.Two_Finger_Vertical_Close_Distance_Thresh",
+};
+
+void setDeviceSpecificConfig(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer) {
+    // There are a great many gesture properties offered by the Gestures library, all of which could
+    // potentially be set in Input Device Configuration files. Maintaining a complete list is
+    // impractical, so instead we only fuzz properties which are used in at least one IDC file, or
+    // which are likely to be used in future (e.g. ones for controlling palm rejection).
+
+    if (fdp.ConsumeBool()) {
+        fuzzer.addProperty("gestureProp.Touchpad_Stack_Version",
+                           std::to_string(fdp.ConsumeIntegral<int>()));
+    }
+
+    for (auto& propertyName : boolPropertiesToFuzz) {
+        if (fdp.ConsumeBool()) {
+            fuzzer.addProperty(propertyName, fdp.ConsumeBool() ? "1" : "0");
+        }
+    }
+
+    for (auto& propertyName : doublePropertiesToFuzz) {
+        if (fdp.ConsumeBool()) {
+            fuzzer.addProperty(propertyName, std::to_string(fdp.ConsumeFloatingPoint<double>()));
+        }
+    }
+
+    if (fdp.ConsumeBool()) {
+        fuzzer.addProperty("gestureProp." + fdp.ConsumeRandomLengthString(),
+                           std::to_string(fdp.ConsumeIntegral<int>()));
+    }
+}
+
+void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfiguration& config) {
+    config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7);
+    config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool();
+    config.touchpadTapToClickEnabled = fdp.ConsumeBool();
+    config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
+}
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+            std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+    setAxisInfos(*fdp, fuzzer);
+    setDeviceSpecificConfig(*fdp, fuzzer);
+
+    InputReaderConfiguration policyConfig;
+    // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
+    // TouchpadInputMapper constructor.
+    setTouchpadSettings(*fdp, policyConfig);
+    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+    TouchpadInputMapper& mapper = fuzzer.getMapper<TouchpadInputMapper>(policyConfig);
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void {
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(info);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    setTouchpadSettings(*fdp, policyConfig);
+                    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+                    std::list<NotifyArgs> unused =
+                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+                                               InputReaderConfiguration::Change(
+                                                       fdp->ConsumeIntegral<uint32_t>()));
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    RawEvent event = getFuzzedRawEvent(*fdp);
+                    std::list<NotifyArgs> unused = mapper.process(&event);
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b34e54f..2523f3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -33,6 +33,7 @@
 
     shared_libs: [
         "libbinder",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libutils",
@@ -40,7 +41,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
     ],
 
     export_shared_lib_headers: [
@@ -48,7 +49,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
     ],
 
     cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index f89035f..9a23c84 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -15,11 +15,11 @@
  */
 
 #define LOG_TAG "PowerHalController"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
 #include <powermanager/PowerHalController.h>
 #include <powermanager/PowerHalLoader.h>
 #include <utils/Log.h>
@@ -33,7 +33,8 @@
 // -------------------------------------------------------------------------------------------------
 
 std::unique_ptr<HalWrapper> HalConnector::connect() {
-    if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) {
+    if (std::shared_ptr<aidl::android::hardware::power::IPower> halAidl =
+                PowerHalLoader::loadAidl()) {
         return std::make_unique<AidlHalWrapper>(halAidl);
     }
     // If V1_0 isn't defined, none of them are
@@ -90,20 +91,24 @@
     return result;
 }
 
-HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost,
+                                             int32_t durationMs) {
     std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setBoost(boost, durationMs);
     return processHalResult(result, "setBoost");
 }
 
-HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
+HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode,
+                                            bool enabled) {
     std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setMode(mode, enabled);
     return processHalResult(result, "setMode");
 }
 
-HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
-        int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+PowerHalController::createHintSession(int32_t tgid, int32_t uid,
+                                      const std::vector<int32_t>& threadIds,
+                                      int64_t durationNanos) {
     std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
     return processHalResult(result, "createHintSession");
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 6bd40f8..2214461 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -16,10 +16,11 @@
 
 #define LOG_TAG "PowerHalLoader"
 
+#include <aidl/android/hardware/power/IPower.h>
+#include <android/binder_manager.h>
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/1.2/IPower.h>
 #include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
 #include <binder/IServiceManager.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
@@ -54,7 +55,7 @@
 // -------------------------------------------------------------------------------------------------
 
 std::mutex PowerHalLoader::gHalMutex;
-sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::gHalAidl = nullptr;
 sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
 sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
 sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
@@ -69,11 +70,30 @@
     gHalHidlV1_3 = nullptr;
 }
 
-sp<IPower> PowerHalLoader::loadAidl() {
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::loadAidl() {
     std::lock_guard<std::mutex> lock(gHalMutex);
     static bool gHalExists = true;
-    static auto loadFn = []() { return waitForVintfService<IPower>(); };
-    return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+    if (!gHalExists) {
+        return nullptr;
+    }
+    if (gHalAidl) {
+        return gHalAidl;
+    }
+    auto aidlServiceName =
+            std::string(aidl::android::hardware::power::IPower::descriptor) + "/default";
+    if (!AServiceManager_isDeclared(aidlServiceName.c_str())) {
+        gHalExists = false;
+        return nullptr;
+    }
+    gHalAidl = aidl::android::hardware::power::IPower::fromBinder(
+            ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str())));
+    if (gHalAidl) {
+        ALOGI("Successfully connected to Power HAL AIDL service.");
+    } else {
+        ALOGI("Power HAL AIDL service not available.");
+        gHalExists = false;
+    }
+    return gHalAidl;
 }
 
 sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 9e7adf8..76afbfc 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -15,86 +15,49 @@
  */
 
 #define LOG_TAG "HalWrapper"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
 #include <cinttypes>
 
 using namespace android::hardware::power;
-namespace Aidl = android::hardware::power;
+namespace Aidl = aidl::android::hardware::power;
 
 namespace android {
 
 namespace power {
 
 // -------------------------------------------------------------------------------------------------
-
-inline HalResult<void> toHalResult(const binder::Status& result) {
+inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) {
     if (result.isOk()) {
         return HalResult<void>::ok();
     }
-    ALOGE("Power HAL request failed: %s", result.toString8().c_str());
-    return HalResult<void>::fromStatus(result);
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
-    return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
-    return ret.isOk() ? HalResult<T>::fromStatus(status, data)
-                      : HalResult<T>::failed(ret.description());
+    ALOGE("Power HAL request failed: %s", result.getDescription().c_str());
+    return HalResult<void>::failed(result.getDescription());
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> HalResult<void>::fromStatus(status_t status) {
-    if (status == android::OK) {
-        return HalResult<void>::ok();
-    }
-    return HalResult<void>::failed(statusToString(status));
-}
-
-HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
-    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
-        return HalResult<void>::unsupported();
-    }
-    if (status.isOk()) {
-        return HalResult<void>::ok();
-    }
-    return HalResult<void>::failed(std::string(status.toString8().c_str()));
-}
-
-template <typename R>
-HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
-    return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
-}
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
     ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
           toString(boost).c_str(), durationMs);
     return HalResult<void>::unsupported();
 }
 
-HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
     ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
           enabled ? "true" : "false");
     return HalResult<void>::unsupported();
 }
 
-HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
         int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
     ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
           threadIds.size());
-    return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+    return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
 }
 
 HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
@@ -104,8 +67,8 @@
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
-    if (boost == Boost::INTERACTION) {
+HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) {
+    if (boost == Aidl::Boost::INTERACTION) {
         return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
     } else {
         ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
@@ -113,20 +76,20 @@
     }
 }
 
-HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) {
     uint32_t data = enabled ? 1 : 0;
     switch (mode) {
-        case Mode::LAUNCH:
+        case Aidl::Mode::LAUNCH:
             return sendPowerHint(V1_3::PowerHint::LAUNCH, data);
-        case Mode::LOW_POWER:
+        case Aidl::Mode::LOW_POWER:
             return sendPowerHint(V1_3::PowerHint::LOW_POWER, data);
-        case Mode::SUSTAINED_PERFORMANCE:
+        case Aidl::Mode::SUSTAINED_PERFORMANCE:
             return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data);
-        case Mode::VR:
+        case Aidl::Mode::VR:
             return sendPowerHint(V1_3::PowerHint::VR_MODE, data);
-        case Mode::INTERACTIVE:
+        case Aidl::Mode::INTERACTIVE:
             return setInteractive(enabled);
-        case Mode::DOUBLE_TAP_TO_WAKE:
+        case Aidl::Mode::DOUBLE_TAP_TO_WAKE:
             return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
         default:
             ALOGV("Skipped setMode %s because Power HAL AIDL not available",
@@ -150,11 +113,11 @@
     return HalResult<void>::fromReturn(ret);
 }
 
-HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
         int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
     ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
           threadIds.size());
-    return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+    return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
 }
 
 HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
@@ -178,26 +141,26 @@
     return HalResult<void>::fromReturn(ret);
 }
 
-HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HidlHalWrapperV1_2::setBoost(Aidl::Boost boost, int32_t durationMs) {
     switch (boost) {
-        case Boost::CAMERA_SHOT:
+        case Aidl::Boost::CAMERA_SHOT:
             return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs);
-        case Boost::CAMERA_LAUNCH:
+        case Aidl::Boost::CAMERA_LAUNCH:
             return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs);
         default:
             return HidlHalWrapperV1_1::setBoost(boost, durationMs);
     }
 }
 
-HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_2::setMode(Aidl::Mode mode, bool enabled) {
     uint32_t data = enabled ? 1 : 0;
     switch (mode) {
-        case Mode::CAMERA_STREAMING_SECURE:
-        case Mode::CAMERA_STREAMING_LOW:
-        case Mode::CAMERA_STREAMING_MID:
-        case Mode::CAMERA_STREAMING_HIGH:
+        case Aidl::Mode::CAMERA_STREAMING_SECURE:
+        case Aidl::Mode::CAMERA_STREAMING_LOW:
+        case Aidl::Mode::CAMERA_STREAMING_MID:
+        case Aidl::Mode::CAMERA_STREAMING_HIGH:
             return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data);
-        case Mode::AUDIO_STREAMING_LOW_LATENCY:
+        case Aidl::Mode::AUDIO_STREAMING_LOW_LATENCY:
             return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data);
         default:
             return HidlHalWrapperV1_1::setMode(mode, enabled);
@@ -206,9 +169,9 @@
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_3::setMode(Aidl::Mode mode, bool enabled) {
     uint32_t data = enabled ? 1 : 0;
-    if (mode == Mode::EXPENSIVE_RENDERING) {
+    if (mode == Aidl::Mode::EXPENSIVE_RENDERING) {
         return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data);
     }
     return HidlHalWrapperV1_2::setMode(mode, enabled);
@@ -222,7 +185,7 @@
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
     std::unique_lock<std::mutex> lock(mBoostMutex);
     size_t idx = static_cast<size_t>(boost);
 
@@ -237,7 +200,7 @@
         auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
         if (!isSupportedRet.isOk()) {
             ALOGE("Skipped setBoost %s because check support failed with: %s",
-                  toString(boost).c_str(), isSupportedRet.toString8().c_str());
+                  toString(boost).c_str(), isSupportedRet.getDescription().c_str());
             // return HalResult::FAILED;
             return HalResult<void>::fromStatus(isSupportedRet);
         }
@@ -254,7 +217,7 @@
     return toHalResult(mHandle->setBoost(boost, durationMs));
 }
 
-HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
     std::unique_lock<std::mutex> lock(mModeMutex);
     size_t idx = static_cast<size_t>(mode);
 
@@ -268,7 +231,7 @@
         bool isSupported = false;
         auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
         if (!isSupportedRet.isOk()) {
-            return HalResult<void>::failed(isSupportedRet.toString8().c_str());
+            return HalResult<void>::failed(isSupportedRet.getDescription());
         }
 
         mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
@@ -283,10 +246,10 @@
     return toHalResult(mHandle->setMode(mode, enabled));
 }
 
-HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
         int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
-    sp<IPowerHintSession> appSession;
-    return HalResult<sp<Aidl::IPowerHintSession>>::
+    std::shared_ptr<Aidl::IPowerHintSession> appSession;
+    return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
             fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
                        appSession);
 }
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 4343aec..03fc38d 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -32,6 +32,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libpowermanager",
@@ -40,7 +41,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
     ],
     static_libs: [
         "libtestUtil",
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 6e5e14d..759485f 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -16,21 +16,24 @@
 
 #define LOG_TAG "PowerHalAidlBenchmarks"
 
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
 #include <benchmark/benchmark.h>
 #include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <powermanager/PowerHalLoader.h>
 #include <testUtil.h>
 #include <chrono>
 
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::WorkDuration;
+using android::power::PowerHalLoader;
 using std::chrono::microseconds;
 
 using namespace android;
@@ -63,15 +66,15 @@
 template <class R, class... Args0, class... Args1>
 static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
                          Args1&&... args1) {
-    sp<IPower> hal = waitForVintfService<IPower>();
+    std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
 
     if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
         return;
     }
 
-    binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
-    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+    ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+    if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
         return;
     }
@@ -79,7 +82,7 @@
     while (state.KeepRunning()) {
         ret = (*hal.*fn)(std::forward<Args1>(args1)...);
         state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
         if (delay > 0us) {
             testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
         }
@@ -90,9 +93,9 @@
 template <class R, class... Args0, class... Args1>
 static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
                                 Args1&&... args1) {
-    sp<IPower> pwHal = waitForVintfService<IPower>();
+    std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
 
-    if (pwHal == nullptr) {
+    if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
         return;
     }
@@ -100,32 +103,32 @@
     // do not use tid from the benchmark process, use 1 for init
     std::vector<int32_t> threadIds{1};
     int64_t durationNanos = 16666666L;
-    sp<IPowerHintSession> hal;
+    std::shared_ptr<IPowerHintSession> session;
 
-    auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+    auto status = hal->createHintSession(1, 0, threadIds, durationNanos, &session);
 
-    if (hal == nullptr) {
+    if (session == nullptr) {
         ALOGV("Power HAL doesn't support session, skipping test...");
         return;
     }
 
-    binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
-    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+    ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...);
+    if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
         return;
     }
 
     while (state.KeepRunning()) {
-        ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+        ret = (*session.*fn)(std::forward<Args1>(args1)...);
         state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
         if (ONEWAY_API_DELAY > 0us) {
             testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
                                   .count());
         }
         state.ResumeTiming();
     }
-    hal->close();
+    session->close();
 }
 
 static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
@@ -155,16 +158,17 @@
     int64_t durationNanos = 16666666L;
     int32_t tgid = 999;
     int32_t uid = 1001;
-    sp<IPowerHintSession> appSession;
-    sp<IPower> hal = waitForVintfService<IPower>();
+    std::shared_ptr<IPowerHintSession> appSession;
+    std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
 
     if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
         return;
     }
 
-    binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
-    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+    ndk::ScopedAStatus ret =
+            hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+    if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
         return;
     }
@@ -172,7 +176,7 @@
     while (state.KeepRunning()) {
         ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
         state.PauseTiming();
-        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
         appSession->close();
         state.ResumeTiming();
     }
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index f8abc7a..effddda 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -16,15 +16,15 @@
 
 #define LOG_TAG "PowerHalControllerBenchmarks"
 
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <benchmark/benchmark.h>
 #include <powermanager/PowerHalController.h>
 #include <testUtil.h>
 #include <chrono>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::power::HalResult;
 using android::power::PowerHalController;
 
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index 167f3a6..111b5d7 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -16,10 +16,10 @@
 
 #define LOG_TAG "PowerHalHidlBenchmarks"
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
 #include <benchmark/benchmark.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
@@ -27,8 +27,6 @@
 #include <chrono>
 
 using android::hardware::Return;
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using android::hardware::power::V1_0::PowerHint;
 using std::chrono::microseconds;
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 54dffcf..08fcdc8 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -43,6 +43,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libpowermanager",
@@ -51,9 +52,10 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
     ],
     static_libs: [
         "libgmock",
     ],
+    require_root: true,
 }
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index 6cc7a6f..01270ce 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -16,9 +16,9 @@
 
 #define LOG_TAG "PowerHalControllerTest"
 
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalController.h>
@@ -26,8 +26,8 @@
 
 #include <thread>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using android::hardware::power::V1_0::IPower;
 using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index e36deed..7d97354 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -16,9 +16,8 @@
 
 #define LOG_TAG "PowerHalLoaderTest"
 
-#include <android-base/logging.h>
+#include <aidl/android/hardware/power/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/IPower.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalLoader.h>
 
@@ -28,7 +27,7 @@
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_2 = android::hardware::power::V1_2::IPower;
 using IPowerV1_3 = android::hardware::power::V1_3::IPower;
-using IPowerAidl = android::hardware::power::IPower;
+using IPowerAidl = aidl::android::hardware::power::IPower;
 
 using namespace android;
 using namespace android::power;
@@ -37,30 +36,30 @@
 // -------------------------------------------------------------------------------------------------
 
 template <typename T>
-sp<T> loadHal();
+T loadHal();
 
 template <>
-sp<IPowerAidl> loadHal<IPowerAidl>() {
+std::shared_ptr<IPowerAidl> loadHal<std::shared_ptr<IPowerAidl>>() {
     return PowerHalLoader::loadAidl();
 }
 
 template <>
-sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+sp<IPowerV1_0> loadHal<sp<IPowerV1_0>>() {
     return PowerHalLoader::loadHidlV1_0();
 }
 
 template <>
-sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+sp<IPowerV1_1> loadHal<sp<IPowerV1_1>>() {
     return PowerHalLoader::loadHidlV1_1();
 }
 
 template <>
-sp<IPowerV1_2> loadHal<IPowerV1_2>() {
+sp<IPowerV1_2> loadHal<sp<IPowerV1_2>>() {
     return PowerHalLoader::loadHidlV1_2();
 }
 
 template <>
-sp<IPowerV1_3> loadHal<IPowerV1_3>() {
+sp<IPowerV1_3> loadHal<sp<IPowerV1_3>>() {
     return PowerHalLoader::loadHidlV1_3();
 }
 
@@ -69,46 +68,47 @@
 template <typename T>
 class PowerHalLoaderTest : public Test {
 public:
-    sp<T> load() { return ::loadHal<T>(); }
+    T load() { return ::loadHal<T>(); }
     void unload() { PowerHalLoader::unloadAll(); }
 };
 
 // -------------------------------------------------------------------------------------------------
-
-typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes;
+typedef ::testing::Types<std::shared_ptr<IPowerAidl>, sp<IPowerV1_0>, sp<IPowerV1_1>,
+                         sp<IPowerV1_2>, sp<IPowerV1_3>>
+        PowerHalTypes;
 TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
 
 TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
-    sp<TypeParam> firstHal = this->load();
+    TypeParam firstHal = this->load();
     if (firstHal == nullptr) {
         ALOGE("Power HAL not available. Skipping test.");
         return;
     }
-    sp<TypeParam> secondHal = this->load();
+    TypeParam secondHal = this->load();
     ASSERT_EQ(firstHal, secondHal);
 }
 
 TYPED_TEST(PowerHalLoaderTest, TestUnload) {
-    sp<TypeParam> firstHal = this->load();
+    TypeParam firstHal = this->load();
     if (firstHal == nullptr) {
         ALOGE("Power HAL not available. Skipping test.");
         return;
     }
     this->unload();
-    sp<TypeParam> secondHal = this->load();
+    TypeParam secondHal = this->load();
     ASSERT_NE(secondHal, nullptr);
     ASSERT_NE(firstHal, secondHal);
 }
 
 TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
-    std::vector<std::future<sp<TypeParam>>> futures;
+    std::vector<std::future<TypeParam>> futures;
     for (int i = 0; i < 10; i++) {
         futures.push_back(
                 std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
     }
 
     futures[0].wait();
-    sp<TypeParam> firstHal = futures[0].get();
+    TypeParam firstHal = futures[0].get();
     if (firstHal == nullptr) {
         ALOGE("Power HAL not available. Skipping test.");
         return;
@@ -116,7 +116,7 @@
 
     for (int i = 1; i < 10; i++) {
         futures[i].wait();
-        sp<TypeParam> currentHal = futures[i].get();
+        TypeParam currentHal = futures[i].get();
         ASSERT_EQ(firstHal, currentHal);
     }
 }
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index cb1a77a..641ba9f 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -16,9 +16,9 @@
 
 #define LOG_TAG "PowerHalWrapperAidlTest"
 
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -28,11 +28,11 @@
 #include <unistd.h>
 #include <thread>
 
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
 using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
 
 using namespace android;
 using namespace android::power;
@@ -43,18 +43,21 @@
 
 class MockIPower : public IPower {
 public:
-    MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
-    MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
-    MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
-    MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
-    MOCK_METHOD(Status, createHintSession,
+    MockIPower() = default;
+
+    MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
                 (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, sp<IPowerHintSession>* session),
+                 int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
                 (override));
-    MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
-    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
-    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
-    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+    MOCK_METHOD(bool, isRemote, (), (override));
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -65,13 +68,13 @@
 
 protected:
     std::unique_ptr<HalWrapper> mWrapper = nullptr;
-    sp<StrictMock<MockIPower>> mMockHal = nullptr;
+    std::shared_ptr<StrictMock<MockIPower>> mMockHal = nullptr;
 };
 
 // -------------------------------------------------------------------------------------------------
 
 void PowerHalWrapperAidlTest::SetUp() {
-    mMockHal = new StrictMock<MockIPower>();
+    mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>();
     mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
     ASSERT_NE(nullptr, mWrapper);
 }
@@ -83,9 +86,11 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
         EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
-                .Times(Exactly(1));
+                .Times(Exactly(1))
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
     }
 
     auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
@@ -97,13 +102,14 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
         EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
     }
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
@@ -115,7 +121,8 @@
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
     EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+            .WillOnce(DoAll(SetArgPointee<1>(false),
+                            Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
     ASSERT_TRUE(result.isUnsupported());
@@ -128,8 +135,13 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+        auto& exp = EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+                            .Times(Exactly(10));
+        for (int i = 0; i < 10; i++) {
+            exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+        }
     }
 
     std::vector<std::thread> threads;
@@ -147,9 +159,11 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
         EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
-                .Times(Exactly(1));
+                .Times(Exactly(1))
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
     }
 
     auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
@@ -161,13 +175,14 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
         EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+                .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
@@ -179,14 +194,16 @@
 TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
     EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+            .WillOnce(DoAll(SetArgPointee<1>(false),
+                            Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
     ASSERT_TRUE(result.isUnsupported());
 
     EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+            .WillOnce(DoAll(SetArgPointee<1>(false),
+                            Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
     result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
     ASSERT_TRUE(result.isUnsupported());
 }
@@ -196,8 +213,13 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+                .WillOnce(DoAll(SetArgPointee<1>(true),
+                                Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+        auto& exp = EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false)))
+                            .Times(Exactly(10));
+        for (int i = 0; i < 10; i++) {
+            exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+        }
     }
 
     std::vector<std::thread> threads;
@@ -217,7 +239,8 @@
     int64_t durationNanos = 16666666L;
     EXPECT_CALL(*mMockHal.get(),
                 createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
-            .Times(Exactly(1));
+            .Times(Exactly(1))
+            .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
     auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
     ASSERT_TRUE(result.isOk());
 }
@@ -230,13 +253,16 @@
     EXPECT_CALL(*mMockHal.get(),
                 createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
             .Times(Exactly(1))
-            .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+            .WillOnce(Return(testing::ByMove(
+                    ndk::ScopedAStatus::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT))));
     auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
     ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
-    EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+    EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+            .Times(Exactly(1))
+            .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
     auto result = mWrapper->getHintSessionPreferredRate();
     ASSERT_TRUE(result.isOk());
     int64_t rate = result.value();
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 0cd2e22..461143b 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -16,17 +16,17 @@
 
 #define LOG_TAG "PowerHalWrapperHidlV1_0Test"
 
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using android::hardware::power::V1_0::IPower;
 using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 32f84e2..79dd996 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -16,18 +16,18 @@
 
 #define LOG_TAG "PowerHalWrapperHidlV1_1Test"
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using android::hardware::power::V1_0::PowerHint;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
index cf48409..aa6d6c7 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
@@ -16,18 +16,18 @@
 
 #define LOG_TAG "PowerHalWrapperHidlV1_2Test"
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android/hardware/power/1.2/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
 using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
index 2c48537..a995afd 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
@@ -16,18 +16,18 @@
 
 #define LOG_TAG "PowerHalWrapperHidlV1_3Test"
 
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
 #include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::hardware::power::V1_0::Feature;
 using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
 using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5683a92..89c80bc 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -27,6 +27,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
+        "libtimestats_deps",
         "surfaceflinger_defaults",
     ],
     cflags: [
@@ -47,7 +48,7 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
@@ -58,14 +59,12 @@
         "libGLESv2",
         "libgui",
         "libhidlbase",
-        "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
-        "libtimestats",
         "libui",
         "libinput",
         "libutils",
@@ -77,11 +76,13 @@
         "libcompositionengine",
         "libframetimeline",
         "libgui_aidl_static",
+        "liblayers_proto",
         "libperfetto_client_experimental",
         "librenderengine",
         "libscheduler",
         "libserviceutils",
         "libshaders",
+        "libtimestats",
         "libtonemap",
     ],
     header_libs: [
@@ -95,6 +96,7 @@
         "libcompositionengine",
         "librenderengine",
         "libserviceutils",
+        "libtimestats",
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
@@ -106,7 +108,6 @@
         "android.hardware.graphics.composer@2.4",
         "libpowermanager",
         "libhidlbase",
-        "libtimestats",
     ],
     // TODO (marissaw): this library is not used by surfaceflinger. This is here so
     // the library compiled in a way that is accessible to system partition when running
@@ -213,14 +214,12 @@
         "-DLOG_TAG=\"SurfaceFlinger\"",
     ],
     shared_libs: [
-        "android.frameworks.displayservice@1.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "libbinder",
         "libcutils",
-        "libdisplayservicehidl",
         "libhidlbase",
         "liblog",
         "libprocessgroup",
@@ -228,6 +227,8 @@
         "libutils",
     ],
     static_libs: [
+        "android.frameworks.displayservice@1.0",
+        "libdisplayservicehidl",
         "libserviceutils",
     ],
 }
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index f3a0186..3426495 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -12,6 +12,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
+        "libtimestats_deps",
         "surfaceflinger_defaults",
     ],
     cflags: [
@@ -26,22 +27,22 @@
         "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
         "libbase",
         "libcutils",
         "libgui",
-        "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libprotobuf-cpp-lite",
         "libSurfaceFlingerProp",
-        "libtimestats",
         "libui",
         "libutils",
     ],
     static_libs: [
+        "liblayers_proto",
         "libmath",
         "librenderengine",
+        "libtimestats",
         "libtonemap",
         "libaidlcommonsupport",
         "libprocessgroup",
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 9c7576e..f00ef67 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,9 +31,9 @@
 #include <utils/Mutex.h>
 #include <utils/Trace.h>
 
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
 
 #include <binder/IServiceManager.h>
 
@@ -49,11 +49,11 @@
 
 namespace impl {
 
-using android::hardware::power::Boost;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::SessionHint;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::WorkDuration;
 
 PowerAdvisor::~PowerAdvisor() = default;
 
@@ -230,7 +230,7 @@
             auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
             if (!ret.isOk()) {
                 ALOGW("Failed to set power hint target work duration with error: %s",
-                      ret.exceptionMessage().c_str());
+                      ret.getDescription().c_str());
                 mHintSessionRunning = false;
             }
         }
@@ -274,7 +274,7 @@
         auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
         if (!ret.isOk()) {
             ALOGW("Failed to report actual work durations with error: %s",
-                  ret.exceptionMessage().c_str());
+                  ret.getDescription().c_str());
             mHintSessionRunning = false;
             return;
         }
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index cfaa135..05e4c8b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,7 +25,7 @@
 #include <ui/FenceTime.h>
 #include <utils/Mutex.h>
 
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <powermanager/PowerHalController.h>
 #include <scheduler/Time.h>
@@ -247,13 +247,14 @@
     bool mHintSessionRunning = false;
 
     std::mutex mHintSessionMutex;
-    sp<hardware::power::IPowerHintSession> mHintSession GUARDED_BY(mHintSessionMutex) = nullptr;
+    std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
+            GUARDED_BY(mHintSessionMutex) = nullptr;
 
     // Initialize to true so we try to call, to check if it's supported
     bool mHasExpensiveRendering = true;
     bool mHasDisplayUpdateImminent = true;
     // Queue of actual durations saved to report
-    std::vector<hardware::power::WorkDuration> mHintSessionQueue;
+    std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue;
     // The latest values we have received for target and actual
     Duration mTargetDuration = kDefaultTargetDuration;
     std::optional<Duration> mActualDuration;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index ded734e..dcc29b9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -140,6 +140,10 @@
         janks.emplace_back("SurfaceFlinger Stuffing");
         jankType &= ~JankType::SurfaceFlingerStuffing;
     }
+    if (jankType & JankType::Dropped) {
+        janks.emplace_back("Dropped Frame");
+        jankType &= ~JankType::Dropped;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -264,6 +268,11 @@
         protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
         jankType &= ~JankType::SurfaceFlingerStuffing;
     }
+    if (jankType & JankType::Dropped) {
+        // Jank dropped does not append to other janks, it fully overrides.
+        protoJank |= FrameTimelineEvent::JANK_DROPPED;
+        jankType &= ~JankType::Dropped;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -365,8 +374,7 @@
 std::optional<int32_t> SurfaceFrame::getJankType() const {
     std::scoped_lock lock(mMutex);
     if (mPresentState == PresentState::Dropped) {
-        // Return no jank if it's a dropped frame since we cannot attribute a jank to a it.
-        return JankType::None;
+        return JankType::Dropped;
     }
     if (mActuals.presentTime == 0) {
         // Frame hasn't been presented yet.
@@ -503,7 +511,8 @@
         // We classify prediction expired as AppDeadlineMissed as the
         // TokenManager::kMaxTokens we store is large enough to account for a
         // reasonable app, so prediction expire would mean a huge scheduling delay.
-        mJankType = JankType::AppDeadlineMissed;
+        mJankType = mPresentState != PresentState::Presented ? JankType::Dropped
+                                                             : JankType::AppDeadlineMissed;
         deadlineDelta = -1;
         return;
     }
@@ -594,17 +603,17 @@
             mJankType |= displayFrameJankType;
         }
     }
+    if (mPresentState != PresentState::Presented) {
+        mJankType = JankType::Dropped;
+        // Since frame was not presented, lets drop any present value
+        mActuals.presentTime = 0;
+    }
 }
 
 void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
                              nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
     std::scoped_lock lock(mMutex);
 
-    if (mPresentState != PresentState::Presented) {
-        // No need to update dropped buffers
-        return;
-    }
-
     mActuals.presentTime = presentTime;
     nsecs_t deadlineDelta = 0;
 
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 163d345..ab4c15d 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -149,13 +149,33 @@
     return debug + "}";
 }
 
-std::string LayerHierarchy::getDebugString(const char* prefix) const {
-    std::string debug = prefix + getDebugStringShort();
-    for (auto& [child, childVariant] : mChildren) {
-        std::string childPrefix = "  " + std::string(prefix) + " " + std::to_string(childVariant);
-        debug += "\n" + child->getDebugString(childPrefix.c_str());
+void LayerHierarchy::dump(std::ostream& out, const std::string& prefix,
+                          LayerHierarchy::Variant variant, bool isLastChild) const {
+    if (!mLayer) {
+        out << " ROOT";
+    } else {
+        out << prefix + (isLastChild ? "└─ " : "├─ ");
+        if (variant == LayerHierarchy::Variant::Relative) {
+            out << "(Relative) ";
+        } else if (variant == LayerHierarchy::Variant::Mirror) {
+            out << "(Mirroring) " << *mLayer << "\n" + prefix + "   └─ ...";
+            return;
+        }
+        out << *mLayer;
     }
-    return debug;
+
+    for (size_t i = 0; i < mChildren.size(); i++) {
+        auto& [child, childVariant] = mChildren[i];
+        if (childVariant == LayerHierarchy::Variant::Detached) continue;
+        const bool lastChild = i == (mChildren.size() - 1);
+        std::string childPrefix = prefix;
+        if (mLayer) {
+            childPrefix += (isLastChild ? "   " : "│  ");
+        }
+        out << "\n";
+        child->dump(out, childPrefix, childVariant, lastChild);
+    }
+    return;
 }
 
 bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 5389ada..1e48387 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -156,7 +156,12 @@
     const RequestedLayerState* getLayer() const;
     const LayerHierarchy* getRelativeParent() const;
     const LayerHierarchy* getParent() const;
-    std::string getDebugString(const char* prefix = "") const;
+    friend std::ostream& operator<<(std::ostream& os, const LayerHierarchy& obj) {
+        std::string prefix = " ";
+        obj.dump(os, prefix, LayerHierarchy::Variant::Attached, /*isLastChild=*/false);
+        return os;
+    }
+
     std::string getDebugStringShort() const;
     // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
     // will contain the first relative root that was visited twice in a traversal.
@@ -172,6 +177,8 @@
     void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
     void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
     void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+    void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
+              bool isLastChild) const;
 
     const RequestedLayerState* mLayer;
     LayerHierarchy* mParent = nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index f0826c6..d389a79 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -39,6 +39,63 @@
     }
 }
 
+std::ostream& operator<<(std::ostream& os, const ui::Transform& transform) {
+    const uint32_t type = transform.getType();
+    const uint32_t orientation = transform.getOrientation();
+    if (type == ui::Transform::IDENTITY) {
+        return os;
+    }
+
+    if (type & ui::Transform::UNKNOWN) {
+        std::string out;
+        transform.dump(out, "", "");
+        os << out;
+        return os;
+    }
+
+    if (type & ui::Transform::ROTATE) {
+        switch (orientation) {
+            case ui::Transform::ROT_0:
+                os << "ROT_0";
+                break;
+            case ui::Transform::FLIP_H:
+                os << "FLIP_H";
+                break;
+            case ui::Transform::FLIP_V:
+                os << "FLIP_V";
+                break;
+            case ui::Transform::ROT_90:
+                os << "ROT_90";
+                break;
+            case ui::Transform::ROT_180:
+                os << "ROT_180";
+                break;
+            case ui::Transform::ROT_270:
+                os << "ROT_270";
+                break;
+            case ui::Transform::ROT_INVALID:
+            default:
+                os << "ROT_INVALID";
+                break;
+        }
+    }
+
+    if (type & ui::Transform::SCALE) {
+        std::string out;
+        android::base::StringAppendF(&out, " scale x=%.4f y=%.4f ", transform.getScaleX(),
+                                     transform.getScaleY());
+        os << out;
+    }
+
+    if (type & ui::Transform::TRANSLATE) {
+        std::string out;
+        android::base::StringAppendF(&out, " tx=%.4f ty=%.4f ", transform.tx(), transform.ty());
+        os << out;
+    }
+
+    return os;
+}
+
 } // namespace
 
 LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
@@ -59,6 +116,7 @@
     }
     sequence = static_cast<int32_t>(state.id);
     name = state.name;
+    debugName = state.debugName;
     textureName = state.textureName;
     premultipliedAlpha = state.premultipliedAlpha;
     inputInfo.name = state.name;
@@ -73,6 +131,8 @@
             ? path
             : LayerHierarchy::TraversalPath::ROOT;
     reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+    frameRateSelectionPriority = state.frameRateSelectionPriority;
+    layerMetadata = state.metadata;
 }
 
 // As documented in libhardware header, formats in the range
@@ -180,13 +240,13 @@
     if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
     if (invalidTransform) return "invalidTransform";
     if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
-    if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
+    if (!hasSomethingToDraw()) return "nothing to draw";
 
     // visible
     std::stringstream reason;
     if (sidebandStream != nullptr) reason << " sidebandStream";
     if (externalTexture != nullptr)
-        reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber;
+        reason << " buffer=" << externalTexture->getId() << " frame=" << frameNumber;
     if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
     if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
     if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
@@ -232,6 +292,36 @@
     return debug.str();
 }
 
+std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) {
+    out << "Layer [" << obj.path.id;
+    if (obj.path.mirrorRootId != UNASSIGNED_LAYER_ID) {
+        out << " mirrored from " << obj.path.mirrorRootId;
+    }
+    out << "] " << obj.name << "\n    " << (obj.isVisible ? "visible" : "invisible")
+        << " reason=" << obj.getIsVisibleReason();
+
+    if (!obj.geomLayerBounds.isEmpty()) {
+        out << "\n    bounds={" << obj.transformedBounds.left << "," << obj.transformedBounds.top
+            << "," << obj.transformedBounds.bottom << "," << obj.transformedBounds.right << "}";
+    }
+
+    if (obj.geomLayerTransform.getType() != ui::Transform::IDENTITY) {
+        out << " toDisplayTransform={" << obj.geomLayerTransform << "}";
+    }
+
+    if (obj.hasInputInfo()) {
+        out << "\n    input{"
+            << "(" << obj.inputInfo.inputConfig.string() << ")";
+        if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId;
+        if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop";
+        auto touchableRegion = obj.inputInfo.touchableRegion.getBounds();
+        out << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+            << touchableRegion.bottom << "," << touchableRegion.right << "}"
+            << "}";
+    }
+    return out;
+}
+
 FloatRect LayerSnapshot::sourceBounds() const {
     if (!externalTexture) {
         return geomLayerBounds;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 2f45d52..1416872 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -67,6 +67,7 @@
     // generated from the same layer, for example when mirroring.
     int32_t sequence;
     std::string name;
+    std::string debugName;
     uint32_t textureName;
     bool contentOpaque;
     bool layerOpaqueFlagSet;
@@ -93,7 +94,7 @@
     ui::Transform::RotationFlags fixedTransformHint;
     std::optional<ui::Transform::RotationFlags> transformHint;
     bool handleSkipScreenshotFlag = false;
-    int32_t frameRateSelectionPriority;
+    int32_t frameRateSelectionPriority = -1;
     LayerHierarchy::TraversalPath mirrorRootPath;
     uint32_t touchCropId;
     gui::Uid uid = gui::Uid::INVALID;
@@ -145,7 +146,7 @@
     bool hasInputInfo() const;
     FloatRect sourceBounds() const;
     Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
-
+    friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
     void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
                bool forceFullDamage, uint32_t displayRotationFlags);
 };
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 21f0a67..3a19d0b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -29,6 +29,7 @@
 
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/Hal.h"
+#include "Layer.h" // eFrameRateSelectionPriority constants
 #include "LayerLog.h"
 #include "LayerSnapshotBuilder.h"
 #include "TimeStats/TimeStats.h"
@@ -819,6 +820,13 @@
                 : parentSnapshot.frameRate;
     }
 
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
+        snapshot.frameRateSelectionPriority =
+                (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
+                ? parentSnapshot.frameRateSelectionPriority
+                : requested.frameRateSelectionPriority;
+    }
+
     if (forceUpdate ||
         snapshot.clientChanges &
                 (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 5738262..a4777d1 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -19,6 +19,7 @@
 #undef LOG_TAG
 #define LOG_TAG "SurfaceFlinger"
 
+#include <gui/TraceUtils.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <sys/types.h>
@@ -126,6 +127,16 @@
     gameMode = gui::GameMode::Unsupported;
     requestedFrameRate = {};
     cachingHint = gui::CachingHint::Enabled;
+
+    if (name.length() > 77) {
+        std::string shortened;
+        shortened.append(name, 0, 36);
+        shortened.append("[...]");
+        shortened.append(name, name.length() - 36);
+        debugName = std::move(shortened);
+    } else {
+        debugName = name;
+    }
 }
 
 void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
@@ -370,6 +381,13 @@
     return debug.str();
 }
 
+std::ostream& operator<<(std::ostream& out, const RequestedLayerState& obj) {
+    out << obj.debugName;
+    if (obj.relativeParentId != UNASSIGNED_LAYER_ID) out << " parent=" << obj.parentId;
+    if (!obj.handleAlive) out << " handleNotAlive";
+    return out;
+}
+
 std::string RequestedLayerState::getDebugStringShort() const {
     return "[" + std::to_string(id) + "]" + name;
 }
@@ -507,6 +525,51 @@
     return changes.test(Changes::Buffer) && !externalTexture;
 }
 
+bool RequestedLayerState::backpressureEnabled() const {
+    return flags & layer_state_t::eEnableBackpressure;
+}
+
+bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
+    static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged;
+    if ((s.what & requiredFlags) != requiredFlags) {
+        ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+                              (s.what | requiredFlags) & ~s.what);
+        return false;
+    }
+
+    static constexpr uint64_t deniedFlags = layer_state_t::eProducerDisconnect |
+            layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+            layer_state_t::eTransparentRegionChanged | layer_state_t::eFlagsChanged |
+            layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
+            layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent;
+    if (s.what & deniedFlags) {
+        ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
+                              s.what & deniedFlags);
+        return false;
+    }
+
+    bool changedFlags = diff(s);
+    static constexpr auto deniedChanges = layer_state_t::ePositionChanged |
+            layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged |
+            layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged |
+            layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+            layer_state_t::eBufferTransformChanged |
+            layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+            layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
+            layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
+            layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged |
+            layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
+            layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
+            layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged;
+    if (changedFlags & deniedChanges) {
+        ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
+                              s.what & deniedChanges);
+        return false;
+    }
+
+    return true;
+}
+
 void RequestedLayerState::clearChanges() {
     what = 0;
     changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 02e3bac..1c19d6d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -74,6 +74,7 @@
     Rect getBufferCrop() const;
     std::string getDebugString() const;
     std::string getDebugStringShort() const;
+    friend std::ostream& operator<<(std::ostream& os, const RequestedLayerState& obj);
     aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
     bool hasValidRelativeParent() const;
     bool hasInputInfo() const;
@@ -82,6 +83,8 @@
     bool hasReadyFrame() const;
     bool hasSidebandStreamFrame() const;
     bool willReleaseBufferOnLatch() const;
+    bool backpressureEnabled() const;
+    bool isSimpleBufferUpdate(const layer_state_t&) const;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
@@ -116,6 +119,7 @@
     uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
     uint64_t barrierFrameNumber = 0;
     uint32_t barrierProducerId = 0;
+    std::string debugName;
 
     // book keeping states
     bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index fa8eb3c..0d3c6eb 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -33,7 +33,7 @@
     ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
 }
 
-std::vector<TransactionState> TransactionHandler::flushTransactions() {
+void TransactionHandler::collectTransactions() {
     while (!mLocklessTransactionQueue.isEmpty()) {
         auto maybeTransaction = mLocklessTransactionQueue.pop();
         if (!maybeTransaction.has_value()) {
@@ -42,7 +42,9 @@
         auto transaction = maybeTransaction.value();
         mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
     }
+}
 
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
     // Collect transaction that are ready to be applied.
     std::vector<TransactionState> transactions;
     TransactionFlushState flushState;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 865835f..04183bc 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -58,6 +58,8 @@
     using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
 
     bool hasPendingTransactions();
+    // Moves transactions from the lockless queue.
+    void collectTransactions();
     std::vector<TransactionState> flushTransactions();
     void addTransactionReadyFilter(TransactionFilter&&);
     void queueTransaction(TransactionState&&);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5a010e8..3a41f15 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -402,7 +402,7 @@
     mLastComputedTrustedPresentationState = false;
 
     if (!leaveState) {
-        const auto outputLayer = findOutputLayerForDisplay(display);
+        const auto outputLayer = findOutputLayerForDisplay(display, snapshot->path);
         if (outputLayer != nullptr) {
             if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) {
                 Region coveredRegion =
@@ -741,6 +741,11 @@
 aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
         const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display);
+    return getCompositionType(outputLayer);
+}
+
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+        const compositionengine::OutputLayer* outputLayer) const {
     if (outputLayer == nullptr) {
         return aidl::android::hardware::graphics::composer3::Composition::INVALID;
     }
@@ -1611,7 +1616,7 @@
     result.append("\n");
 }
 
-void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display);
     if (!outputLayer) {
         return;
@@ -1662,6 +1667,41 @@
     result.append("\n");
 }
 
+void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
+                     const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
+    if (!outputLayer) {
+        return;
+    }
+
+    StringAppendF(&result, " %s\n", snapshot.debugName.c_str());
+    StringAppendF(&result, "  %10zu | ", snapshot.globalZ);
+    StringAppendF(&result, "  %10d | ",
+                  snapshot.layerMetadata.getInt32(gui::METADATA_WINDOW_TYPE, 0));
+    StringAppendF(&result, "%10s | ", toString(getCompositionType(outputLayer)).c_str());
+    const auto& outputLayerState = outputLayer->getState();
+    StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
+    const Rect& frame = outputLayerState.displayFrame;
+    StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
+    const FloatRect& crop = outputLayerState.sourceCrop;
+    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
+                  crop.bottom);
+    const auto frameRate = snapshot.frameRate;
+    if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
+        StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
+                      ftl::enum_string(frameRate.type).c_str(),
+                      ftl::enum_string(frameRate.seamlessness).c_str());
+    } else {
+        result.append(41, ' ');
+    }
+
+    const auto focused = isLayerFocusedBasedOnPriority(snapshot.frameRateSelectionPriority);
+    StringAppendF(&result, "    [%s]\n", focused ? "*" : " ");
+
+    result.append(kDumpTableRowLength, '-');
+    result.append("\n");
+}
+
 void Layer::dumpFrameStats(std::string& result) const {
     mFrameTracker.dumpStats(result);
 }
@@ -2578,6 +2618,24 @@
     return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
 }
 
+compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
+        const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const {
+    if (!display) return nullptr;
+    if (!mFlinger->mLayerLifecycleManagerEnabled) {
+        return display->getCompositionDisplay()->getOutputLayerForLayer(
+                getCompositionEngineLayerFE());
+    }
+    sp<LayerFE> layerFE;
+    for (auto& [p, layer] : mLayerFEs) {
+        if (p == path) {
+            layerFE = layer;
+        }
+    }
+
+    if (!layerFE) return nullptr;
+    return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
+}
+
 Region Layer::getVisibleRegion(const DisplayDevice* display) const {
     const auto outputLayer = findOutputLayerForDisplay(display);
     return outputLayer ? outputLayer->getState().visibleRegion : Region();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2fbbbdc..5d77657 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -343,6 +343,7 @@
     virtual sp<LayerFE> getCompositionEngineLayerFE() const;
     virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
     sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
+    sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
 
     const frontend::LayerSnapshot* getLayerSnapshot() const;
     frontend::LayerSnapshot* editLayerSnapshot();
@@ -692,7 +693,8 @@
 
     gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
 
-    void miniDump(std::string& result, const DisplayDevice&) const;
+    void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
+    void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
     void dumpOffscreenDebugInfo(std::string& result) const;
     void clearFrameStats();
@@ -960,6 +962,8 @@
     void addZOrderRelative(const wp<Layer>& relative);
     void removeZOrderRelative(const wp<Layer>& relative);
     compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+    compositionengine::OutputLayer* findOutputLayerForDisplay(
+            const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const;
     bool usingRelativeZ(LayerVector::StateSet) const;
 
     virtual ui::Transform getInputTransform() const;
@@ -1064,7 +1068,8 @@
 
     aidl::android::hardware::graphics::composer3::Composition getCompositionType(
             const DisplayDevice&) const;
-
+    aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+            const compositionengine::OutputLayer*) const;
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
      * That includes the current layer and all its descendants.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f1fd6db..607bec2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -148,7 +148,8 @@
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
                             bufferStatus);
 
-        sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+        sk_sp<SkSurface> surface = SkSurfaces::Raster(
+                SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
         SkCanvas* canvas = surface->getCanvas();
         canvas->setMatrix(canvasTransform);
 
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index badbf53..3b61de7 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -29,6 +29,7 @@
     virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
+    virtual void onChoreographerAttached() = 0;
 
 protected:
     ~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index beaf972..5d00a26 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -131,22 +131,6 @@
     const auto& info = layerPair->second;
     info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
-    // Set frame rate to attached choreographer.
-    // TODO(b/260898223): Change to use layer hierarchy and handle frame rate vote.
-    if (updateType == LayerUpdateType::SetFrameRate) {
-        auto range = mAttachedChoreographers.equal_range(id);
-        auto it = range.first;
-        while (it != range.second) {
-            sp<EventThreadConnection> choreographerConnection = it->second.promote();
-            if (choreographerConnection) {
-                choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
-                it++;
-            } else {
-                it = mAttachedChoreographers.erase(it);
-            }
-        }
-    }
-
     // Activate layer if inactive.
     if (found == LayerStatus::LayerInInactiveMap) {
         mActiveLayerInfos.insert(
@@ -301,12 +285,6 @@
     return 0.f;
 }
 
-void LayerHistory::attachChoreographer(int32_t layerId,
-                                       const sp<EventThreadConnection>& choreographerConnection) {
-    std::lock_guard lock(mLock);
-    mAttachedChoreographers.insert({layerId, wp<EventThreadConnection>(choreographerConnection)});
-}
-
 auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
     // the layer could be in either the active or inactive map, try both
     auto it = mActiveLayerInfos.find(id);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 69caf9f..d083fa2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -84,9 +84,6 @@
     // return the frames per second of the layer with the given sequence id.
     float getLayerFramerate(nsecs_t now, int32_t id) const;
 
-    void attachChoreographer(int32_t layerId,
-                             const sp<EventThreadConnection>& choreographerConnection);
-
 private:
     friend class LayerHistoryTest;
     friend class TestableScheduler;
@@ -124,10 +121,6 @@
     LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
     LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
 
-    // Map keyed by layer ID (sequence) to choreographer connections.
-    std::unordered_multimap<int32_t, wp<EventThreadConnection>> mAttachedChoreographers
-            GUARDED_BY(mLock);
-
     uint32_t mDisplayArea = 0;
 
     // Whether to emit systrace output and debug logs.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index f136e9f..7951c33 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -853,6 +853,8 @@
                                       return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
                                   });
         ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
+        ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
+                              to_string(overrideFps).c_str(), uid);
         frameRateOverrides.emplace(uid, overrideFps);
     }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 41639b6..0a1bd4c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -260,29 +260,40 @@
     const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
     ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
 
-    auto connection = createConnectionInternal(eventThread.get());
+    auto connection = eventThread->createEventConnection([&] { resync(); });
 
     std::lock_guard<std::mutex> lock(mConnectionsLock);
     mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
     return handle;
 }
 
-sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, EventRegistrationFlags eventRegistration,
-        const sp<IBinder>& layerHandle) {
-    int32_t layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
-    auto connection = eventThread->createEventConnection([&] { resync(); }, eventRegistration);
-    mLayerHistory.attachChoreographer(layerId, connection);
-    return connection;
-}
-
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
         ConnectionHandle handle, EventRegistrationFlags eventRegistration,
         const sp<IBinder>& layerHandle) {
-    std::lock_guard<std::mutex> lock(mConnectionsLock);
-    RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration,
-                                    layerHandle);
+    const auto connection = [&]() -> sp<EventThreadConnection> {
+        std::scoped_lock lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle, nullptr);
+
+        return mConnections[handle].thread->createEventConnection([&] { resync(); },
+                                                                  eventRegistration);
+    }();
+    const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
+
+    if (layerId != static_cast<int32_t>(UNASSIGNED_LAYER_ID)) {
+        // TODO(b/290409668): Moving the choreographer attachment to be a transaction that will be
+        // processed on the main thread.
+        mSchedulerCallback.onChoreographerAttached();
+
+        std::scoped_lock lock(mChoreographerLock);
+        const auto [iter, emplaced] =
+                mAttachedChoreographers.emplace(layerId,
+                                                AttachedChoreographers{Fps(), {connection}});
+        if (!emplaced) {
+            iter->second.connections.emplace(connection);
+            connection->frameRate = iter->second.frameRate;
+        }
+    }
+    return connection;
 }
 
 sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -555,6 +566,11 @@
     mLayerHistory.deregisterLayer(layer);
 }
 
+void Scheduler::onLayerDestroyed(Layer* layer) {
+    std::scoped_lock lock(mChoreographerLock);
+    mAttachedChoreographers.erase(layer->getSequence());
+}
+
 void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
     if (pacesetterSelectorPtr()->canSwitch()) {
@@ -571,7 +587,9 @@
                                                    mFeatures.test(Feature::kContentDetection));
 }
 
-void Scheduler::chooseRefreshRateForContent() {
+void Scheduler::chooseRefreshRateForContent(
+        const surfaceflinger::frontend::LayerHierarchy* hierarchy,
+        bool updateAttachedChoreographer) {
     const auto selectorPtr = pacesetterSelectorPtr();
     if (!selectorPtr->canSwitch()) return;
 
@@ -579,6 +597,20 @@
 
     LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
     applyPolicy(&Policy::contentRequirements, std::move(summary));
+
+    if (updateAttachedChoreographer) {
+        LOG_ALWAYS_FATAL_IF(!hierarchy);
+
+        // update the attached choreographers after we selected the render rate.
+        const ftl::Optional<FrameRateMode> modeOpt = [&] {
+            std::scoped_lock lock(mPolicyLock);
+            return mPolicy.modeOpt;
+        }();
+
+        if (modeOpt) {
+            updateAttachedChoreographers(*hierarchy, modeOpt->fps);
+        }
+    }
 }
 
 void Scheduler::resetIdleTimer() {
@@ -822,6 +854,105 @@
     mPolicy = {};
 }
 
+void Scheduler::updateAttachedChoreographersFrameRate(
+        const surfaceflinger::frontend::RequestedLayerState& layer, Fps fps) {
+    std::scoped_lock lock(mChoreographerLock);
+
+    const auto layerId = static_cast<int32_t>(layer.id);
+    const auto choreographers = mAttachedChoreographers.find(layerId);
+    if (choreographers == mAttachedChoreographers.end()) {
+        return;
+    }
+
+    auto& layerChoreographers = choreographers->second;
+
+    layerChoreographers.frameRate = fps;
+    ATRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+    ALOGV("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+
+    auto it = layerChoreographers.connections.begin();
+    while (it != layerChoreographers.connections.end()) {
+        sp<EventThreadConnection> choreographerConnection = it->promote();
+        if (choreographerConnection) {
+            choreographerConnection->frameRate = fps;
+            it++;
+        } else {
+            it = choreographers->second.connections.erase(it);
+        }
+    }
+
+    if (layerChoreographers.connections.empty()) {
+        mAttachedChoreographers.erase(choreographers);
+    }
+}
+
+int Scheduler::updateAttachedChoreographersInternal(
+        const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate,
+        int parentDivisor) {
+    const char* name = layerHierarchy.getLayer() ? layerHierarchy.getLayer()->name.c_str() : "Root";
+
+    int divisor = 0;
+    if (layerHierarchy.getLayer()) {
+        const auto frameRateCompatibility = layerHierarchy.getLayer()->frameRateCompatibility;
+        const auto frameRate = Fps::fromValue(layerHierarchy.getLayer()->frameRate);
+        ALOGV("%s: %s frameRate %s parentDivisor=%d", __func__, name, to_string(frameRate).c_str(),
+              parentDivisor);
+
+        if (frameRate.isValid()) {
+            if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ||
+                frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_EXACT) {
+                // Since this layer wants an exact match, we would only set a frame rate if the
+                // desired rate is a divisor of the display refresh rate.
+                divisor = RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate);
+            } else if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
+                // find the closest frame rate divisor for the desired frame rate.
+                divisor = static_cast<int>(
+                        std::round(displayRefreshRate.getValue() / frameRate.getValue()));
+            }
+        }
+    }
+
+    // We start by traversing the children, updating their choreographers, and getting back the
+    // aggregated frame rate.
+    int childrenDivisor = 0;
+    for (const auto& [child, _] : layerHierarchy.mChildren) {
+        LOG_ALWAYS_FATAL_IF(child == nullptr || child->getLayer() == nullptr);
+
+        ALOGV("%s: %s traversing child %s", __func__, name, child->getLayer()->name.c_str());
+
+        const int childDivisor =
+                updateAttachedChoreographersInternal(*child, displayRefreshRate, divisor);
+        childrenDivisor = childrenDivisor > 0 ? childrenDivisor : childDivisor;
+        if (childDivisor > 0) {
+            childrenDivisor = std::gcd(childrenDivisor, childDivisor);
+        }
+        ALOGV("%s: %s childrenDivisor=%d", __func__, name, childrenDivisor);
+    }
+
+    ALOGV("%s: %s divisor=%d", __func__, name, divisor);
+
+    // If there is no explicit vote for this layer. Use the children's vote if exists
+    divisor = (divisor == 0) ? childrenDivisor : divisor;
+    ALOGV("%s: %s divisor=%d with children", __func__, name, divisor);
+
+    // If there is no explicit vote for this layer or its children, Use the parent vote if exists
+    divisor = (divisor == 0) ? parentDivisor : divisor;
+    ALOGV("%s: %s divisor=%d with parent", __func__, name, divisor);
+
+    if (layerHierarchy.getLayer()) {
+        Fps fps = divisor > 1 ? displayRefreshRate / (unsigned int)divisor : Fps();
+        updateAttachedChoreographersFrameRate(*layerHierarchy.getLayer(), fps);
+    }
+
+    return divisor;
+}
+
+void Scheduler::updateAttachedChoreographers(
+        const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate) {
+    ATRACE_CALL();
+    updateAttachedChoreographersInternal(layerHierarchy, displayRefreshRate, 0);
+}
+
 template <typename S, typename T>
 auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
     ATRACE_CALL();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 17e9cea..c44c447 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -52,6 +52,8 @@
 #include "Utils/Dumper.h"
 #include "VsyncModulator.h"
 
+#include <FrontEnd/LayerHierarchy.h>
+
 namespace android::scheduler {
 
 // Opaque handle to scheduler connection.
@@ -153,7 +155,7 @@
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ConnectionHandle, EventRegistrationFlags eventRegistration = {},
-            const sp<IBinder>& layerHandle = nullptr);
+            const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
 
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
@@ -230,9 +232,11 @@
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
+    void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
+    void chooseRefreshRateForContent(const surfaceflinger::frontend::LayerHierarchy*,
+                                     bool updateAttachedChoreographer) EXCLUDES(mDisplayLock);
 
     void resetIdleTimer();
 
@@ -310,9 +314,6 @@
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
-    sp<EventThreadConnection> createConnectionInternal(
-            EventThread*, EventRegistrationFlags eventRegistration = {},
-            const sp<IBinder>& layerHandle = nullptr);
 
     // Update feature state machine to given state when corresponding timer resets or expires.
     void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
@@ -386,6 +387,12 @@
     GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
+    void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
+                                      Fps displayRefreshRate);
+    int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
+                                             Fps displayRefreshRate, int parentDivisor);
+    void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
+                                               Fps fps) EXCLUDES(mChoreographerLock);
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
 
@@ -506,6 +513,16 @@
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mPolicy GUARDED_BY(mPolicyLock);
 
+    std::mutex mChoreographerLock;
+
+    struct AttachedChoreographers {
+        Fps frameRate;
+        std::unordered_set<wp<EventThreadConnection>, WpHash> connections;
+    };
+    // Map keyed by layer ID (sequence) to choreographer connections.
+    std::unordered_map<int32_t, AttachedChoreographers> mAttachedChoreographers
+            GUARDED_BY(mChoreographerLock);
+
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
             GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 0757b57..47e92e1 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -21,6 +21,7 @@
 #include <string>
 
 #include <android-base/thread_annotations.h>
+#include <ThreadContext.h>
 #include <ftl/enum.h>
 #include <ftl/optional.h>
 #include <ui/DisplayId.h>
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8ffc3c3..2782ce4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -24,6 +24,7 @@
 
 #include "SurfaceFlinger.h"
 
+#include <aidl/android/hardware/power/Boost.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -34,7 +35,6 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/Boost.h>
 #include <android/native_window.h>
 #include <android/os/IInputFlinger.h>
 #include <binder/IPCThreadState.h>
@@ -326,6 +326,7 @@
 bool SurfaceFlinger::useHwcForRgbToYuv;
 bool SurfaceFlinger::hasSyncFramework;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+int64_t SurfaceFlinger::minAcquiredBuffers = 1;
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::useContextPriority;
@@ -377,6 +378,7 @@
 }
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
+    ATRACE_CALL();
     ALOGI("SurfaceFlinger is starting");
 
     hasSyncFramework = running_without_sync_framework(true);
@@ -386,6 +388,8 @@
     useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
 
     maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
+    minAcquiredBuffers =
+            SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
 
     maxGraphicsWidth = std::max(max_graphics_width(0), 0);
     maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -493,10 +497,6 @@
         return LatchUnsignaledConfig::AutoSingleLayer;
     }
 
-    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
-        return LatchUnsignaledConfig::Always;
-    }
-
     return LatchUnsignaledConfig::Disabled;
 }
 
@@ -690,7 +690,7 @@
 
     // wait patiently for the window manager death
     const String16 name("window");
-    mWindowManager = defaultServiceManager()->getService(name);
+    mWindowManager = defaultServiceManager()->waitForService(name);
     if (mWindowManager != 0) {
         mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this));
     }
@@ -704,7 +704,7 @@
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
-    sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+    sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
 
     static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
         if (input == nullptr) {
@@ -803,6 +803,7 @@
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
 void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
+    ATRACE_CALL();
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
     addTransactionReadyFilters();
@@ -1945,8 +1946,21 @@
                        float currentDimmingRatio =
                                compositionDisplay->editState().sdrWhitePointNits /
                                compositionDisplay->editState().displayBrightnessNits;
-                       compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
-                                                                brightness.displayBrightnessNits);
+                       static constexpr float kDimmingThreshold = 0.02f;
+                       if (brightness.sdrWhitePointNits == 0.f ||
+                           abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) /
+                                           brightness.sdrWhitePointNits >=
+                                   kDimmingThreshold) {
+                           // to optimize, skip brightness setter if the brightness difference ratio
+                           // is lower than threshold
+                           compositionDisplay
+                                   ->setDisplayBrightness(brightness.sdrWhitePointNits,
+                                                          brightness.displayBrightnessNits);
+                       } else {
+                           compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
+                                                                    brightness.sdrWhitePointNits);
+                       }
+
                        FTL_FAKE_GUARD(kMainThreadContext,
                                       display->stageBrightness(brightness.displayBrightness));
 
@@ -2020,7 +2034,7 @@
 }
 
 status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
-    using hardware::power::Boost;
+    using aidl::android::hardware::power::Boost;
     Boost powerBoost = static_cast<Boost>(boostId);
 
     if (powerBoost == Boost::INTERACTION) {
@@ -2169,12 +2183,22 @@
     }
 }
 
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
-                                                bool transactionsFlushed,
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
+                                                bool flushTransactions,
                                                 bool& outTransactionsAreEmpty) {
     ATRACE_CALL();
+    frontend::Update update;
+    if (flushTransactions) {
+        update = flushLifecycleUpdates();
+        if (mTransactionTracing) {
+            mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+                                                          update, mFrontEndDisplayInfos,
+                                                          mFrontEndDisplayInfosChanged);
+        }
+    }
+
     bool needsTraversal = false;
-    if (transactionsFlushed) {
+    if (flushTransactions) {
         needsTraversal |= commitMirrorDisplays(vsyncId);
         needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
         needsTraversal |= applyTransactions(update.transactions, vsyncId);
@@ -2222,12 +2246,43 @@
     }
 }
 
-bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
-                                          bool transactionsFlushed, bool& outTransactionsAreEmpty) {
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
+                                          bool flushTransactions, bool& outTransactionsAreEmpty) {
     using Changes = frontend::RequestedLayerState::Changes;
     ATRACE_CALL();
-    {
+    frontend::Update update;
+    if (flushTransactions) {
+        ATRACE_NAME("TransactionHandler:flushTransactions");
+        // Locking:
+        // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+        // we must keep a copy of the transactions (specifically the composer
+        // states) around outside the scope of the lock.
+        // 2. Transactions and created layers do not share a lock. To prevent applying
+        // transactions with layers still in the createdLayer queue, collect the transactions
+        // before committing the created layers.
+        // 3. Transactions can only be flushed after adding layers, since the layer can be a newly
+        // created one
+        mTransactionHandler.collectTransactions();
+        {
+            // TODO(b/238781169) lockless queue this and keep order.
+            std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+            update.layerCreatedStates = std::move(mCreatedLayers);
+            mCreatedLayers.clear();
+            update.newLayers = std::move(mNewLayers);
+            mNewLayers.clear();
+            update.layerCreationArgs = std::move(mNewLayerArgs);
+            mNewLayerArgs.clear();
+            update.destroyedHandles = std::move(mDestroyedHandles);
+            mDestroyedHandles.clear();
+        }
+
         mLayerLifecycleManager.addLayers(std::move(update.newLayers));
+        update.transactions = mTransactionHandler.flushTransactions();
+        if (mTransactionTracing) {
+            mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+                                                          update, mFrontEndDisplayInfos,
+                                                          mFrontEndDisplayInfosChanged);
+        }
         mLayerLifecycleManager.applyTransactions(update.transactions);
         mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
         for (auto& legacyLayer : update.layerCreatedStates) {
@@ -2236,11 +2291,11 @@
                 mLegacyLayers[layer->sequence] = layer;
             }
         }
-    }
-    if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
-        ATRACE_NAME("LayerHierarchyBuilder:update");
-        mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
-                                      mLayerLifecycleManager.getDestroyedLayers());
+        if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
+            ATRACE_NAME("LayerHierarchyBuilder:update");
+            mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
+                                          mLayerLifecycleManager.getDestroyedLayers());
+        }
     }
 
     bool mustComposite = false;
@@ -2270,6 +2325,11 @@
                                                       Changes::Visibility)) {
         mVisibleRegionsDirty = true;
     }
+    if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
+        // The frame rate of attached choreographers can only change as a result of a
+        // FrameRate change (including when Hierarchy changes).
+        mUpdateAttachedChoreographer = true;
+    }
     outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
     mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
 
@@ -2414,25 +2474,16 @@
                                     Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
         const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
-        frontend::Update updates;
-        if (flushTransactions) {
-            updates = flushLifecycleUpdates();
-            if (mTransactionTracing) {
-                mTransactionTracing
-                        ->addCommittedTransactions(ftl::to_underlying(vsyncId),
-                                                   pacesetterFrameTarget.frameBeginTime().ns(),
-                                                   updates, mFrontEndDisplayInfos,
-                                                   mFrontEndDisplayInfosChanged);
-            }
-        }
         bool transactionsAreEmpty;
         if (mLegacyFrontEndEnabled) {
-            mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
-                                                        transactionsAreEmpty);
+            mustComposite |=
+                    updateLayerSnapshotsLegacy(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+                                               flushTransactions, transactionsAreEmpty);
         }
         if (mLayerLifecycleManagerEnabled) {
             mustComposite |=
-                    updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty);
+                    updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+                                         flushTransactions, transactionsAreEmpty);
         }
 
         if (transactionFlushNeeded()) {
@@ -2455,8 +2506,14 @@
     // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
     // and may eventually call to ~Layer() if it holds the last reference
     {
+        bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
+        mUpdateAttachedChoreographer = false;
+
         Mutex::Autolock lock(mStateLock);
-        mScheduler->chooseRefreshRateForContent();
+        mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled
+                                                        ? &mLayerHierarchyBuilder.getHierarchy()
+                                                        : nullptr,
+                                                updateAttachedChoreographer);
         setActiveModeInHwcIfNeeded();
     }
 
@@ -2859,31 +2916,55 @@
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
             int32_t maxArea = 0;
-            mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
-                const auto layerFe = layer->getCompositionEngineLayerFE();
-                const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
-                if (snapshot.isVisible &&
-                    compositionDisplay->includesLayer(snapshot.outputFilter)) {
-                    if (isHdrLayer(snapshot)) {
-                        const auto* outputLayer =
-                            compositionDisplay->getOutputLayerForLayer(layerFe);
-                        if (outputLayer) {
-                            const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
-                                    ? std::numeric_limits<float>::infinity()
-                                    : snapshot.desiredHdrSdrRatio;
-                            info.mergeDesiredRatio(desiredHdrSdrRatio);
-                            info.numberOfHdrLayers++;
-                            const auto displayFrame = outputLayer->getState().displayFrame;
-                            const int32_t area = displayFrame.width() * displayFrame.height();
-                            if (area > maxArea) {
-                                maxArea = area;
-                                info.maxW = displayFrame.width();
-                                info.maxH = displayFrame.height();
+            auto updateInfoFn =
+                    [&](const std::shared_ptr<compositionengine::Display>& compositionDisplay,
+                        const frontend::LayerSnapshot& snapshot, const sp<LayerFE>& layerFe) {
+                        if (snapshot.isVisible &&
+                            compositionDisplay->includesLayer(snapshot.outputFilter)) {
+                            if (isHdrLayer(snapshot)) {
+                                const auto* outputLayer =
+                                        compositionDisplay->getOutputLayerForLayer(layerFe);
+                                if (outputLayer) {
+                                    const float desiredHdrSdrRatio =
+                                            snapshot.desiredHdrSdrRatio <= 1.f
+                                            ? std::numeric_limits<float>::infinity()
+                                            : snapshot.desiredHdrSdrRatio;
+                                    info.mergeDesiredRatio(desiredHdrSdrRatio);
+                                    info.numberOfHdrLayers++;
+                                    const auto displayFrame = outputLayer->getState().displayFrame;
+                                    const int32_t area =
+                                            displayFrame.width() * displayFrame.height();
+                                    if (area > maxArea) {
+                                        maxArea = area;
+                                        info.maxW = displayFrame.width();
+                                        info.maxH = displayFrame.height();
+                                    }
+                                }
                             }
                         }
-                    }
-                }
-            });
+                    };
+
+            if (mLayerLifecycleManagerEnabled) {
+                mLayerSnapshotBuilder.forEachVisibleSnapshot(
+                        [&, compositionDisplay = compositionDisplay](
+                                std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+                            auto it = mLegacyLayers.find(snapshot->sequence);
+                            LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+                                                "Couldnt find layer object for %s",
+                                                snapshot->getDebugString().c_str());
+                            auto& legacyLayer = it->second;
+                            sp<LayerFE> layerFe =
+                                    legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+
+                            updateInfoFn(compositionDisplay, *snapshot, layerFe);
+                        });
+            } else {
+                mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+                    const auto layerFe = layer->getCompositionEngineLayerFE();
+                    const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+                    updateInfoFn(compositionDisplay, snapshot, layerFe);
+                });
+            }
             listener->dispatchHdrLayerInfo(info);
         }
     }
@@ -3854,6 +3935,14 @@
     mPowerAdvisor->notifyCpuLoadUp();
 }
 
+void SurfaceFlinger::onChoreographerAttached() {
+    ATRACE_CALL();
+    if (mLayerLifecycleManagerEnabled) {
+        mUpdateAttachedChoreographer = true;
+        scheduleCommit(FrameHint::kNone);
+    }
+}
+
 void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
     using namespace scheduler;
 
@@ -4236,18 +4325,16 @@
     return TransactionReadiness::Ready;
 }
 
-TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy(
         const TransactionHandler::TransactionFlushState& flushState) {
     using TransactionReadiness = TransactionHandler::TransactionReadiness;
     auto ready = TransactionReadiness::Ready;
-    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s,
-                                                                   const std::shared_ptr<
-                                                                           renderengine::
-                                                                                   ExternalTexture>&
-                                                                           externalTexture)
-                                                                       -> bool {
-        sp<Layer> layer = LayerHandle::getLayer(s.surface);
+    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+                                                                           resolvedState) -> bool {
+        sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
+
         const auto& transaction = *flushState.transaction;
+        const auto& s = resolvedState.state;
         // check for barrier frames
         if (s.bufferData->hasBarrier) {
             // The current producerId is already a newer producer than the buffer that has a
@@ -4255,7 +4342,7 @@
             // don't wait on the barrier since we know that's stale information.
             if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
                 layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
-                                                 externalTexture->getBuffer(),
+                                                 resolvedState.externalTexture->getBuffer(),
                                                  s.bufferData->frameNumber,
                                                  s.bufferData->acquireFence);
                 // Delete the entire state at this point and not just release the buffer because
@@ -4292,18 +4379,17 @@
             return TraverseBuffersReturnValues::STOP_TRAVERSAL;
         }
 
-        // ignore the acquire fence if LatchUnsignaledConfig::Always is set.
-        const bool checkAcquireFence = enableLatchUnsignaledConfig != LatchUnsignaledConfig::Always;
         const bool acquireFenceAvailable = s.bufferData &&
                 s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
                 s.bufferData->acquireFence;
-        const bool fenceSignaled = !checkAcquireFence || !acquireFenceAvailable ||
+        const bool fenceSignaled = !acquireFenceAvailable ||
                 s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
         if (!fenceSignaled) {
             // check fence status
-            const bool allowLatchUnsignaled =
-                    shouldLatchUnsignaled(layer, s, transaction.states.size(),
-                                          flushState.firstTransaction);
+            const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+                                                                    flushState.firstTransaction) &&
+                    layer->isSimpleBufferUpdate(s);
+
             if (allowLatchUnsignaled) {
                 ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
                               layer->getDebugName());
@@ -4328,15 +4414,118 @@
     return ready;
 }
 
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    auto ready = TransactionReadiness::Ready;
+    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+                                                                           resolvedState) -> bool {
+        const frontend::RequestedLayerState* layer =
+                mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
+        const auto& transaction = *flushState.transaction;
+        const auto& s = resolvedState.state;
+        // check for barrier frames
+        if (s.bufferData->hasBarrier) {
+            // The current producerId is already a newer producer than the buffer that has a
+            // barrier. This means the incoming buffer is older and we can release it here. We
+            // don't wait on the barrier since we know that's stale information.
+            if (layer->barrierProducerId > s.bufferData->producerId) {
+                if (s.bufferData->releaseBufferListener) {
+                    uint32_t currentMaxAcquiredBufferCount =
+                            getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val());
+                    ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+                                          layer->name.c_str(), s.bufferData->frameNumber);
+                    s.bufferData->releaseBufferListener
+                            ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(),
+                                               s.bufferData->frameNumber},
+                                              s.bufferData->acquireFence
+                                                      ? s.bufferData->acquireFence
+                                                      : Fence::NO_FENCE,
+                                              currentMaxAcquiredBufferCount);
+                }
+
+                // Delete the entire state at this point and not just release the buffer because
+                // everything associated with the Layer in this Transaction is now out of date.
+                ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(),
+                              layer->barrierProducerId, s.bufferData->producerId);
+                return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
+            }
+
+            if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+                const bool willApplyBarrierFrame =
+                        flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+                        ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+                          s.bufferData->barrierFrameNumber));
+                if (!willApplyBarrierFrame) {
+                    ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+                                  layer->name.c_str(), layer->barrierFrameNumber,
+                                  s.bufferData->barrierFrameNumber);
+                    ready = TransactionReadiness::NotReadyBarrier;
+                    return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+                }
+            }
+        }
+
+        // If backpressure is enabled and we already have a buffer to commit, keep
+        // the transaction in the queue.
+        const bool hasPendingBuffer =
+                flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+        if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+            ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+            ready = TransactionReadiness::NotReady;
+            return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+        }
+
+        const bool acquireFenceAvailable = s.bufferData &&
+                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+                s.bufferData->acquireFence;
+        const bool fenceSignaled = !acquireFenceAvailable ||
+                s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+        if (!fenceSignaled) {
+            // check fence status
+            const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+                                                                    flushState.firstTransaction) &&
+                    layer->isSimpleBufferUpdate(s);
+            if (allowLatchUnsignaled) {
+                ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str());
+                ready = TransactionReadiness::NotReadyUnsignaled;
+            } else {
+                ready = TransactionReadiness::NotReady;
+                auto& listener = s.bufferData->releaseBufferListener;
+                if (listener &&
+                    (flushState.queueProcessTime - transaction.postTime) >
+                            std::chrono::nanoseconds(4s).count()) {
+                    mTransactionHandler
+                            .onTransactionQueueStalled(transaction.id, listener,
+                                                       "Buffer processing hung up due to stuck "
+                                                       "fence. Indicates GPU hang");
+                }
+                ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+                return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+            }
+        }
+        return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+    });
+    return ready;
+}
+
 void SurfaceFlinger::addTransactionReadyFilters() {
     mTransactionHandler.addTransactionReadyFilter(
             std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
-    mTransactionHandler.addTransactionReadyFilter(
-            std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
+    if (mLayerLifecycleManagerEnabled) {
+        mTransactionHandler.addTransactionReadyFilter(
+                std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this,
+                          std::placeholders::_1));
+    } else {
+        mTransactionHandler.addTransactionReadyFilter(
+                std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this,
+                          std::placeholders::_1));
+    }
 }
 
 // For tests only
 bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
+    mTransactionHandler.collectTransactions();
     std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
     return applyTransactions(transactions, vsyncId);
 }
@@ -4389,18 +4578,13 @@
             predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
 }
 
-bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
-                                           size_t numStates, bool firstTransaction) const {
+bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates,
+                                           bool firstTransaction) const {
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
         ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
         return false;
     }
 
-    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
-        ATRACE_FORMAT_INSTANT("%s: true (LatchUnsignaledConfig::Always)", __func__);
-        return true;
-    }
-
     // We only want to latch unsignaled when a single layer is updated in this
     // transaction (i.e. not a blast sync transaction).
     if (numStates != 1) {
@@ -4427,7 +4611,7 @@
         }
     }
 
-    return layer->isSimpleBufferUpdate(state);
+    return true;
 }
 
 status_t SurfaceFlinger::setTransactionState(
@@ -5557,7 +5741,7 @@
                 {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
                 {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
                 {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
-                {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
+                {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
                 {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
@@ -5575,17 +5759,56 @@
         // traversals, which can result in use-after-frees.
         std::string compositionLayers;
         mScheduler
-                ->schedule([&] {
-                    StringAppendF(&compositionLayers, "Composition layers\n");
-                    mDrawingState.traverseInZOrder([&](Layer* layer) {
-                        auto* compositionState = layer->getCompositionState();
-                        if (!compositionState || !compositionState->isVisible) return;
+                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                    if (!mLayerLifecycleManagerEnabled) {
+                        StringAppendF(&compositionLayers, "Composition layers\n");
+                        mDrawingState.traverseInZOrder([&](Layer* layer) {
+                            auto* compositionState = layer->getCompositionState();
+                            if (!compositionState || !compositionState->isVisible) return;
+                            android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n",
+                                                         layer,
+                                                         layer->getDebugName()
+                                                                 ? layer->getDebugName()
+                                                                 : "<unknown>");
+                            compositionState->dump(compositionLayers);
+                        });
+                    } else {
+                        std::ostringstream out;
+                        out << "\nComposition list\n";
+                        ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+                        mLayerSnapshotBuilder.forEachVisibleSnapshot(
+                                [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+                                    if (snapshot->hasSomethingToDraw()) {
+                                        if (lastPrintedLayerStackHeader !=
+                                            snapshot->outputFilter.layerStack) {
+                                            lastPrintedLayerStackHeader =
+                                                    snapshot->outputFilter.layerStack;
+                                            out << "LayerStack=" << lastPrintedLayerStackHeader.id
+                                                << "\n";
+                                        }
+                                        out << "  " << *snapshot << "\n";
+                                    }
+                                });
 
-                        android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
-                                                     layer->getDebugName() ? layer->getDebugName()
-                                                                           : "<unknown>");
-                        compositionState->dump(compositionLayers);
-                    });
+                        out << "\nInput list\n";
+                        lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+                        mLayerSnapshotBuilder.forEachInputSnapshot(
+                                [&](const frontend::LayerSnapshot& snapshot) {
+                                    if (lastPrintedLayerStackHeader !=
+                                        snapshot.outputFilter.layerStack) {
+                                        lastPrintedLayerStackHeader =
+                                                snapshot.outputFilter.layerStack;
+                                        out << "LayerStack=" << lastPrintedLayerStackHeader.id
+                                            << "\n";
+                                    }
+                                    out << "  " << snapshot << "\n";
+                                });
+
+                        out << "\nLayer Hierarchy\n"
+                            << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+                        compositionLayers = out.str();
+                        dumpHwcLayersMinidump(compositionLayers);
+                    }
                 })
                 .get();
 
@@ -5915,7 +6138,7 @@
     result.append(future.get());
 }
 
-void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const {
+void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         const auto displayId = HalDisplayId::tryCast(display->getId());
         if (!displayId) {
@@ -5927,7 +6150,33 @@
         Layer::miniDumpHeader(result);
 
         const DisplayDevice& ref = *display;
-        mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+        mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); });
+        result.append("\n");
+    }
+}
+
+void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = HalDisplayId::tryCast(display->getId());
+        if (!displayId) {
+            continue;
+        }
+
+        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+                      displayId == mActiveDisplayId ? "active" : "inactive");
+        Layer::miniDumpHeader(result);
+
+        const DisplayDevice& ref = *display;
+        mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+            if (!snapshot.hasSomethingToDraw() ||
+                ref.getLayerStack() != snapshot.outputFilter.layerStack) {
+                return;
+            }
+            auto it = mLegacyLayers.find(snapshot.sequence);
+            LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+                                snapshot.getDebugString().c_str());
+            it->second->miniDump(result, snapshot, ref);
+        });
         result.append("\n");
     }
 }
@@ -5976,7 +6225,10 @@
      * Dump the visible layer list
      */
     colorizer.bold(result);
-    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
+    StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n",
+                  mLayerLifecycleManagerEnabled ? "true" : "false");
+    StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n",
+                  mNumLayers.load());
     colorizer.reset(result);
 
     result.append(compositionLayers);
@@ -6049,7 +6301,9 @@
     }
     result.push_back('\n');
 
-    dumpHwcLayersMinidumpLocked(result);
+    if (mLegacyFrontEndEnabled) {
+        dumpHwcLayersMinidumpLockedLegacy(result);
+    }
 
     {
         DumpArgs plannerArgs;
@@ -7703,6 +7957,7 @@
     if (mTransactionTracing) {
         mTransactionTracing->onLayerRemoved(layer->getSequence());
     }
+    mScheduler->onLayerDestroyed(layer);
 }
 
 void SurfaceFlinger::onLayerUpdate() {
@@ -7798,7 +8053,7 @@
     if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
         pipelineDepth++;
     }
-    return std::max(1ll, pipelineDepth - 1);
+    return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -8272,6 +8527,7 @@
     // 2. Transactions and created layers do not share a lock. To prevent applying
     // transactions with layers still in the createdLayer queue, flush the transactions
     // before committing the created layers.
+    mTransactionHandler.collectTransactions();
     update.transactions = mTransactionHandler.flushTransactions();
     {
         // TODO(b/238781169) lockless queue this and keep order.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e27d21f..1947072 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -227,6 +227,10 @@
     // FramebufferSurface
     static int64_t maxFrameBufferAcquiredBuffers;
 
+    // Controls the minimum acquired buffers SurfaceFlinger will suggest via
+    // ISurfaceComposer.getMaxAcquiredBufferCount().
+    static int64_t minAcquiredBuffers;
+
     // Controls the maximum width and height in pixels that the graphics pipeline can support for
     // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
     static uint32_t maxGraphicsWidth;
@@ -642,6 +646,7 @@
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
     void kernelTimerChanged(bool expired) override;
     void triggerOnFrameRateOverridesChanged() override;
+    void onChoreographerAttached() override;
 
     // ICEPowerCallback overrides:
     void notifyCpuLoadUp() override;
@@ -714,10 +719,9 @@
             compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
     void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
                                           const std::vector<std::pair<Layer*, LayerFE*>>& layers);
-    bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
-                                    bool transactionsFlushed, bool& out)
-            REQUIRES(kMainThreadContext);
-    bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
+    bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
+                                    bool& out) REQUIRES(kMainThreadContext);
+    bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
                               bool& out) REQUIRES(kMainThreadContext);
     void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
     frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
@@ -761,6 +765,9 @@
     TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
             const TransactionHandler::TransactionFlushState& flushState)
             REQUIRES(kMainThreadContext);
+    TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
     TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
             const TransactionHandler::TransactionFlushState& flushState)
             REQUIRES(kMainThreadContext);
@@ -785,8 +792,7 @@
     void commitOffscreenLayers();
 
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
-    bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
-                               bool firstTransaction) const;
+    bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
     bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1044,7 +1050,8 @@
      */
     void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
                        std::string& result) const REQUIRES(mStateLock);
-    void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
+    void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
+    void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
     void listLayersLocked(std::string& result) const;
@@ -1175,6 +1182,7 @@
     bool mUpdateInputInfo = false;
     bool mSomeChildrenChanged;
     bool mForceTransactionDisplayChange = false;
+    bool mUpdateAttachedChoreographer = false;
 
     // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
     bool mLayerMetadataSnapshotNeeded = false;
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 4686eed..c3141be 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -7,14 +7,9 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library {
-    name: "libtimestats",
-    srcs: [
-        "TimeStats.cpp",
-    ],
-    header_libs: [
-        "libscheduler_headers",
-    ],
+cc_defaults {
+    name: "libtimestats_deps",
+
     shared_libs: [
         "android.hardware.graphics.composer@2.4",
         "libbase",
@@ -22,17 +17,34 @@
         "liblog",
         "libprotobuf-cpp-lite",
         "libtimestats_atoms_proto",
-        "libtimestats_proto",
         "libui",
         "libutils",
     ],
+
+    static_libs: [
+        "libtimestats_proto",
+    ],
+
+    export_static_lib_headers: [
+        "libtimestats_proto",
+    ],
+}
+
+cc_library {
+    name: "libtimestats",
+    defaults: [
+        "libtimestats_deps",
+    ],
+    srcs: [
+        "TimeStats.cpp",
+    ],
+    header_libs: [
+        "libscheduler_headers",
+    ],
     export_include_dirs: ["."],
     export_header_lib_headers: [
         "libscheduler_headers",
     ],
-    export_shared_lib_headers: [
-        "libtimestats_proto",
-    ],
     cppflags: [
         "-Wall",
         "-Werror",
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index cf1ca65..cbbcb91 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -115,7 +115,7 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     result.append("Jank payload for this layer:\n");
     result.append(jankPayload.toString());
-    result.append("SetFrateRate vote for this layer:\n");
+    result.append("SetFrameRate vote for this layer:\n");
     result.append(setFrameRateVote.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7132a59..31cd2d7 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -89,7 +89,7 @@
     void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
         for (auto state = states.begin(); state != states.end();) {
             if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
-                int result = visitor(state->state, state->externalTexture);
+                int result = visitor(*state);
                 if (result == STOP_TRAVERSAL) return;
                 if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
                     state = states.erase(state);
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index f76a8d7..0f9060d 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -138,3 +138,18 @@
         "surfaceflinger_frametracer_fuzzer.cpp",
     ],
 }
+
+cc_fuzz {
+    name: "surfaceflinger_service_fuzzer",
+    defaults: [
+        "surfaceflinger_fuzz_defaults",
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    srcs: [
+        "surfaceflinger_service_fuzzer.cpp",
+    ],
+    fuzz_config: {
+        triage_assignee: "waghpawan@google.com",
+    },
+}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 80943b5..b2d4131 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -24,7 +24,6 @@
 namespace android::fuzz {
 
 static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = {
-        LatchUnsignaledConfig::Always,
         LatchUnsignaledConfig::AutoSingleLayer,
         LatchUnsignaledConfig::Disabled,
 };
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 0c9a16b..00e92a1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -792,6 +792,7 @@
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
+    void onChoreographerAttached() override {}
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger =
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
new file mode 100644
index 0000000..849a896
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerDefaultFactory.h"
+
+using namespace android;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    DefaultFactory factory;
+    sp<SurfaceFlinger> flinger = sp<SurfaceFlinger>::make(factory);
+    flinger->init();
+
+    sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
+    fuzzService({flinger, composerAIDL}, FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 689f51a..5cac086 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -472,6 +472,16 @@
     prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
 }
 
+# Controls the minimum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+    api_name: "min_acquired_buffers"
+    type: Long
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.min_acquired_buffers"
+}
+
 # When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
 # buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
 # Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
@@ -484,4 +494,3 @@
     access: Readonly
     prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
 }
-
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9660ff3..ba88acc 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -97,6 +97,11 @@
     prop_name: "ro.surface_flinger.max_virtual_display_dimension"
   }
   prop {
+    api_name: "min_acquired_buffers"
+    type: Long
+    prop_name: "ro.surface_flinger.min_acquired_buffers"
+  }
+  prop {
     api_name: "present_time_offset_from_vsync_ns"
     type: Long
     prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 40a5d57..18bd3b9 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -289,7 +289,7 @@
             IPCThreadState::self()->joinThreadPool();
             [&]() { exit(0); }();
         }
-        sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+        sp<IBinder> binder = defaultServiceManager()->waitForService(serviceName);
         remote = interface_cast<IIPCTest>(binder);
         remote->setDeathToken(mDeathRecipient);
     }
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
index 7077523..f1bb231 100644
--- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -85,7 +85,7 @@
     ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90inactive) {
     auto displayToken = mOuterDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
     mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -95,7 +95,7 @@
     ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothInnerActive) {
     auto displayToken = mInnerDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
     mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -110,7 +110,7 @@
     ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
 }
 
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothOuterActive) {
     mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
     auto displayToken = mInnerDisplay->getDisplayToken().promote();
     mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 86af303..fa5fa95 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -161,7 +161,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-cpp",
+        "android.hardware.power-V4-ndk",
         "libaidlcommonsupport",
         "libcompositionengine_mocks",
         "libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d26ef3c..8911430 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -1198,7 +1198,7 @@
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -1234,8 +1234,8 @@
     auto protoDroppedSurfaceFrameActualStart =
             createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
                                                displayFrameToken1, sPidOne, sLayerNameOne,
-                                               FrameTimelineEvent::PRESENT_DROPPED, false, false,
-                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PRESENT_DROPPED, true, false,
+                                               FrameTimelineEvent::JANK_DROPPED,
                                                FrameTimelineEvent::PREDICTION_VALID, true);
     auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
 
@@ -1470,7 +1470,7 @@
             createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_DROPPED, false, false,
-                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::JANK_DROPPED,
                                                FrameTimelineEvent::PREDICTION_EXPIRED, true);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 4301186..3234483 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -308,6 +308,17 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRateSelectionPriority = priority;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     LayerLifecycleManager mLifecycleManager;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 5da893e..d6c4b72 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -20,6 +20,7 @@
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
 #include "FrontEnd/LayerSnapshotBuilder.h"
+#include "Layer.h"
 #include "LayerHierarchyTest.h"
 
 #define UPDATE_AND_VERIFY(BUILDER, ...)                                    \
@@ -467,4 +468,48 @@
     EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
 }
 
+TEST_F(LayerSnapshotTest, snashotContainsMetadataFromLayerCreationArgs) {
+    LayerCreationArgs args(std::make_optional<uint32_t>(200));
+    args.name = "testlayer";
+    args.addToRoot = true;
+    args.metadata.setInt32(42, 24);
+
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+    EXPECT_TRUE(layers.back()->metadata.has(42));
+    EXPECT_EQ(layers.back()->metadata.getInt32(42, 0), 24);
+    mLifecycleManager.addLayers(std::move(layers));
+
+    std::vector<uint32_t> expected = STARTING_ZORDER;
+    expected.push_back(200);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+    EXPECT_TRUE(mSnapshotBuilder.getSnapshot(200)->layerMetadata.has(42));
+    EXPECT_EQ(mSnapshotBuilder.getSnapshot(200)->layerMetadata.getInt32(42, 0), 24);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionPriorityPassedToChildLayers) {
+    setFrameRateSelectionPriority(11, 1);
+
+    setFrameRateSelectionPriority(12, 2);
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 2);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 2);
+
+    // reparent and verify the child gets the new parent's framerate selection priority
+    reparentLayer(122, 11);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 1);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 1);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 0d66d59..85f66f4 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -18,10 +18,10 @@
 #define LOG_TAG "PowerAdvisorTest"
 
 #include <DisplayHardware/PowerAdvisor.h>
-#include <compositionengine/Display.h>
-#include <ftl/fake_guard.h>
+#include <binder/Status.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
 #include <ui/DisplayId.h>
 #include <chrono>
 #include "TestableSurfaceFlinger.h"
@@ -50,7 +50,7 @@
     TestableSurfaceFlinger mFlinger;
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
     MockPowerHalController* mMockPowerHalController;
-    sp<MockIPowerHintSession> mMockPowerHintSession;
+    std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
 };
 
 void PowerAdvisorTest::SetUp() {
@@ -64,13 +64,14 @@
 
 void PowerAdvisorTest::startPowerHintSession() {
     const std::vector<int32_t> threadIds = {1, 2, 3};
-    mMockPowerHintSession = android::sp<NiceMock<MockIPowerHintSession>>::make();
+    mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
     ON_CALL(*mMockPowerHalController, createHintSession)
-            .WillByDefault(
-                    Return(HalResult<sp<IPowerHintSession>>::fromStatus(binder::Status::ok(),
-                                                                        mMockPowerHintSession)));
+            .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+                                          fromStatus(binder::Status::ok(), mMockPowerHintSession)));
     mPowerAdvisor->enablePowerHintSession(true);
     mPowerAdvisor->startPowerHintSession(threadIds);
+    ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
+            .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
 }
 
 void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
@@ -123,8 +124,8 @@
     EXPECT_CALL(*mMockPowerHintSession,
                 reportActualWorkDuration(ElementsAre(
                         Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
-            .Times(1);
-
+            .Times(1)
+            .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
     mPowerAdvisor->setDisplays(displayIds);
@@ -163,7 +164,8 @@
     EXPECT_CALL(*mMockPowerHintSession,
                 reportActualWorkDuration(ElementsAre(
                         Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
-            .Times(1);
+            .Times(1)
+            .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
 
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -205,7 +207,8 @@
     EXPECT_CALL(*mMockPowerHintSession,
                 reportActualWorkDuration(ElementsAre(
                         Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
-            .Times(1);
+            .Times(1)
+            .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
 
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 682c998..405f79d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -30,6 +30,10 @@
 #include "mock/MockLayer.h"
 #include "mock/MockSchedulerCallback.h"
 
+#include <FrontEnd/LayerHierarchy.h>
+
+#include "FpsOps.h"
+
 namespace android::scheduler {
 
 using android::mock::createDisplayMode;
@@ -42,6 +46,10 @@
 using MockEventThread = android::mock::EventThread;
 using MockLayer = android::mock::MockLayer;
 
+using LayerHierarchy = surfaceflinger::frontend::LayerHierarchy;
+using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
+using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;
+
 class SchedulerTest : public testing::Test {
 protected:
     class MockEventThreadConnection : public android::EventThreadConnection {
@@ -83,6 +91,7 @@
 
     mock::SchedulerCallback mSchedulerCallback;
     TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
+    surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
 
     ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
@@ -199,7 +208,8 @@
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
-    mScheduler->chooseRefreshRateForContent();
+    mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+                                            /*updateAttachedChoreographer*/ false);
 }
 
 TEST_F(SchedulerTest, updateDisplayModes) {
@@ -249,6 +259,11 @@
     EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
 
     EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+
+    const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
+    mFlinger.mutableMinAcquiredBuffers() = 2;
+    EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+    mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
 }
 
 MATCHER(Is120Hz, "") {
@@ -273,11 +288,13 @@
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
-    mScheduler->chooseRefreshRateForContent();
+    mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+                                            /*updateAttachedChoreographer*/ false);
 
     // No-op if layer requirements have not changed.
     EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
-    mScheduler->chooseRefreshRateForContent();
+    mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+                                            /*updateAttachedChoreographer*/ false);
 }
 
 TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
@@ -432,4 +449,344 @@
     }
 }
 
+class AttachedChoreographerTest : public SchedulerTest {
+protected:
+    void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
+                               Fps expectedChoreographerFps);
+};
+
+TEST_F(AttachedChoreographerTest, registerSingle) {
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    const sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+    EXPECT_EQ(1u,
+              mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+    EXPECT_FALSE(
+            mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) {
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    const auto handle = layer->getHandle();
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+
+    EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_))
+            .WillOnce(Return(0))
+            .WillOnce(Return(0));
+
+    const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread);
+    const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread);
+    EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+            .WillOnce(Return(mockConnection1))
+            .WillOnce(Return(mockConnection2));
+
+    const sp<IDisplayEventConnection> connection1 =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+    const sp<IDisplayEventConnection> connection2 =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+
+    EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+    EXPECT_EQ(2u,
+              mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+    EXPECT_FALSE(
+            mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnDifferentLayers) {
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+    const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+    const sp<IDisplayEventConnection> connection1 =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle());
+    const sp<IDisplayEventConnection> connection2 =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle());
+
+    EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size());
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer1->getSequence()));
+    EXPECT_EQ(1u,
+              mScheduler->mutableAttachedChoreographers()[layer1->getSequence()]
+                      .connections.size());
+    EXPECT_FALSE(
+            mScheduler->mutableAttachedChoreographers()[layer1->getSequence()].frameRate.isValid());
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer2->getSequence()));
+    EXPECT_EQ(1u,
+              mScheduler->mutableAttachedChoreographers()[layer2->getSequence()]
+                      .connections.size());
+    EXPECT_FALSE(
+            mScheduler->mutableAttachedChoreographers()[layer2->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenConnectionIsGone) {
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+    EXPECT_EQ(1u,
+              mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+
+    // The connection is used all over this test, so it is quite hard to release it from here.
+    // Instead, we just do a small shortcut.
+    {
+        EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
+        sp<MockEventThreadConnection> mockConnection =
+                sp<MockEventThreadConnection>::make(mEventThread);
+        mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.clear();
+        mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.emplace(
+                mockConnection);
+    }
+
+    RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+    LayerHierarchy hierarchy(&layerState);
+    mScheduler->updateAttachedChoreographers(hierarchy, 60_Hz);
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) {
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+    sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    const sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    layer.clear();
+    mFlinger.mutableLayersPendingRemoval().clear();
+    EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+void AttachedChoreographerTest::frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility,
+                                                      Fps displayFps,
+                                                      Fps expectedChoreographerFps) {
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+    LayerHierarchy hierarchy(&layerState);
+
+    layerState.frameRate = layerFps.getValue();
+    layerState.frameRateCompatibility = frameRateCompatibility;
+
+    mScheduler->updateAttachedChoreographers(hierarchy, displayFps);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+    EXPECT_EQ(expectedChoreographerFps,
+              mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+    EXPECT_EQ(expectedChoreographerFps, mEventThreadConnection->frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateDefault) {
+    Fps layerFps = 30_Hz;
+    int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+    Fps displayFps = 60_Hz;
+    Fps expectedChoreographerFps = 30_Hz;
+
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+    layerFps = Fps::fromValue(32.7f);
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExact) {
+    Fps layerFps = 30_Hz;
+    int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_EXACT;
+    Fps displayFps = 60_Hz;
+    Fps expectedChoreographerFps = 30_Hz;
+
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+    layerFps = Fps::fromValue(32.7f);
+    expectedChoreographerFps = {};
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExactOrMultiple) {
+    Fps layerFps = 30_Hz;
+    int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+    Fps displayFps = 60_Hz;
+    Fps expectedChoreographerFps = 30_Hz;
+
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+    layerFps = Fps::fromValue(32.7f);
+    expectedChoreographerFps = {};
+    frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent) {
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+    RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+    LayerHierarchy parentHierarchy(&parentState);
+
+    RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+    LayerHierarchy hierarchy(&layerState);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+    layerState.frameRate = (30_Hz).getValue();
+    layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+    EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent2Children) {
+    const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+    RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+    LayerHierarchy parentHierarchy(&parentState);
+
+    RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+    LayerHierarchy layer1Hierarchy(&layer1State);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+    RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+    LayerHierarchy layer2Hierarchy(&layer2State);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+    layer1State.frameRate = (30_Hz).getValue();
+    layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    layer2State.frameRate = (20_Hz).getValue();
+    layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+    EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParentConflictingChildren) {
+    const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+    RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+    LayerHierarchy parentHierarchy(&parentState);
+
+    RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+    LayerHierarchy layer1Hierarchy(&layer1State);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+    RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+    LayerHierarchy layer2Hierarchy(&layer2State);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+    layer1State.frameRate = (30_Hz).getValue();
+    layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    layer2State.frameRate = (25_Hz).getValue();
+    layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+    EXPECT_EQ(Fps(), mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChild) {
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+    LayerHierarchy parentHierarchy(&parentState);
+
+    RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+    LayerHierarchy hierarchy(&layerState);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+    parentState.frameRate = (30_Hz).getValue();
+    parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+    EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) {
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+    EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+    sp<IDisplayEventConnection> connection =
+            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+    RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+    LayerHierarchy parentHierarchy(&parentState);
+
+    RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+    LayerHierarchy hierarchy(&layerState);
+    parentHierarchy.mChildren.push_back(
+            std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+    parentState.frameRate = (30_Hz).getValue();
+    parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    layerState.frameRate = (60_Hz).getValue();
+    layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+    mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+    ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+    EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 4e9f293..22b72f9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -23,12 +23,12 @@
 #include "DisplayTransactionTestHelpers.h"
 #include "FakeDisplayInjector.h"
 
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
 
 namespace android {
 namespace {
 
-using android::hardware::power::Boost;
+using aidl::android::hardware::power::Boost;
 
 TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
     using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index aac11c0..fa7a947 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -39,7 +39,7 @@
     TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
                               std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
-                              /* modulatorPtr */ nullptr, callback) {}
+                              sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
@@ -104,6 +104,7 @@
 
     auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
     auto& mutableLayerHistory() { return mLayerHistory; }
+    auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
@@ -168,6 +169,12 @@
                                           : VsyncSchedule::HwVsyncState::Disabled;
     }
 
+    void updateAttachedChoreographers(
+            const surfaceflinger::frontend::LayerHierarchy& layerHierarchy,
+            Fps displayRefreshRate) {
+        Scheduler::updateAttachedChoreographers(layerHierarchy, displayRefreshRate);
+    }
+
     using Scheduler::onHardwareVsyncRequest;
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 833984f..5be4548 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -622,6 +622,10 @@
         return SurfaceFlinger::sActiveDisplayRotationFlags;
     }
 
+    auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+
+    auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+
     auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
 
     ~TestableSurfaceFlinger() {
@@ -632,6 +636,7 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
+        mFlinger->mLayersPendingRemoval.clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index afb8efb..644b8c7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -15,7 +15,7 @@
  */
 
 #undef LOG_TAG
-#define LOG_TAG "CompositionTest"
+#define LOG_TAG "TransactionApplicationTest"
 
 #include <compositionengine/Display.h>
 #include <compositionengine/mock/DisplaySurface.h>
@@ -855,233 +855,13 @@
                          kExpectedTransactionsPending);
 }
 
-class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
-public:
-    void SetUp() override {
-        LatchUnsignaledTest::SetUp();
-        SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    }
-};
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {createComposerState(kLayerId, fence(Fence::Status::Signaled),
-                                                       layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
-                                                       layer_state_t::eBufferChanged)});
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto mixedTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
-                                                       layer_state_t::eBufferChanged),
-                                   createComposerState(kLayerId, fence(Fence::Status::Signaled),
-                                                       layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto mixedTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
-                                                       layer_state_t::eBufferChanged),
-                                   createComposerState(kLayerId2, fence(Fence::Status::Signaled),
-                                                       layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto signaledTransaction2 =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken2,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({signaledTransaction, unsignaledTransaction},
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyToken) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction, signaledTransaction},
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto unsignaledTransaction2 =
-            createTransactionInfo(kApplyToken2,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, RespectsBackPressureFlag) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kExpectedTransactionsPending = 1u;
-    auto layer =
-            sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}));
-    auto layerHandle = layer->getHandle();
-    const auto setBackPressureFlagTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
-                                                       layer_state_t::eBufferChanged |
-                                                               layer_state_t::eFlagsChanged,
-                                                       {layerHandle})});
-    setTransactionStates({setBackPressureFlagTransaction}, 0u);
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged,
-                                                              {layerHandle}),
-                                  });
-    const auto unsignaledTransaction2 =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged,
-                                                              {layerHandle}),
-                                  });
-    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsPending = 0u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-
-    modulateVsync();
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
 TEST(TransactionHandlerTest, QueueTransaction) {
     TransactionHandler handler;
     TransactionState transaction;
     transaction.applyToken = sp<BBinder>::make();
     transaction.id = 42;
     handler.queueTransaction(std::move(transaction));
+    handler.collectTransactions();
     std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
 
     EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index 0ddc90d..a088aab 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -18,14 +18,14 @@
 
 #include "binder/Status.h"
 
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
 #include <gmock/gmock.h>
 
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
 using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
 
 namespace android::Hwc2::mock {
 
@@ -33,18 +33,19 @@
 public:
     MockIPower();
 
-    MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
-    MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
-    MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
-    MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
-    MOCK_METHOD(Status, createHintSession,
+    MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
                 (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, sp<IPowerHintSession>* session),
+                 int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
                 (override));
-    MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
-    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
-    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
-    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+    MOCK_METHOD(bool, isRemote, (), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index f4ded21..2b9520f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -18,14 +18,14 @@
 
 #include "binder/Status.h"
 
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
 #include <gmock/gmock.h>
 
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionHint;
 using android::binder::Status;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::SessionHint;
 
-using namespace android::hardware::power;
+using namespace aidl::android::hardware::power;
 
 namespace android::Hwc2::mock {
 
@@ -33,16 +33,18 @@
 public:
     MockIPowerHintSession();
 
-    MOCK_METHOD(IBinder*, onAsBinder, (), (override));
-    MOCK_METHOD(Status, pause, (), (override));
-    MOCK_METHOD(Status, resume, (), (override));
-    MOCK_METHOD(Status, close, (), (override));
-    MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
-    MOCK_METHOD(std::string, getInterfaceHash, (), (override));
-    MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
-    MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
-    MOCK_METHOD(Status, sendHint, (SessionHint), (override));
-    MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override));
+    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+    MOCK_METHOD(bool, isRemote, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index 8e22f43..68fe3c5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -31,8 +31,8 @@
 
 namespace android::Hwc2::mock {
 
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
 using android::power::HalResult;
 
 class MockPowerHalController : public power::PowerHalController {
@@ -42,8 +42,9 @@
     MOCK_METHOD(void, init, (), (override));
     MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override));
     MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override));
-    MOCK_METHOD(HalResult<sp<hardware::power::IPowerHintSession>>, createHintSession,
-                (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override));
+    MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+                createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
+                (override));
     MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 306eb4d..22b2ccc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -27,6 +27,7 @@
     MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
+    MOCK_METHOD(void, onChoreographerAttached, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -34,6 +35,7 @@
     void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
+    void onChoreographerAttached() override {}
 };
 
 } // namespace android::scheduler::mock
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index aaeb8f9..4c0910a 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -102,18 +102,6 @@
     ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
 }
 
-TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 5; i++) {
-        threads.push_back(std::thread(
-                [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    ASSERT_TRUE(waitForCallbacks(5, 25ms));
-    ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
-}
-
 TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
     mScheduler->schedule(createCallback(1), 5ms);
     mScheduler.reset(nullptr);