Merge changes I2a4359de,I08376f69 into main

* changes:
  Revert "Disable stylus integration tests temporarily"
  EventHub: Track the global key and switch states for enabled devices
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index c6132e8..b302f52 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -236,6 +236,16 @@
     }                                                       \
 }
 
+// we could have tighter checks, but this is only to avoid hard errors. Negative values are defined
+// in UserHandle.java and carry specific meanings that may not be handled by certain APIs here.
+#define ENFORCE_VALID_USER(userId)                                     \
+    {                                                                  \
+        if (static_cast<uid_t>(std::abs(userId)) >=                    \
+            std::numeric_limits<uid_t>::max() / AID_USER_OFFSET) {     \
+            return error("userId invalid: " + std::to_string(userId)); \
+        }                                                              \
+    }
+
 #define CHECK_ARGUMENT_UUID(uuid) {                         \
     binder::Status status = checkArgumentUuid((uuid));      \
     if (!status.isOk()) {                                   \
@@ -696,6 +706,7 @@
         int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
         int32_t targetSdkVersion, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
 
@@ -790,6 +801,8 @@
 binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t appId, int32_t flags) {
+    ENFORCE_VALID_USER(userId);
+
     int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
     if (sdkSandboxUid == -1) {
         // There no valid sdk sandbox process for this app. Skip creation of data directory
@@ -828,6 +841,7 @@
         int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
         int32_t targetSdkVersion, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -839,6 +853,7 @@
         const android::os::CreateAppDataArgs& args,
         android::os::CreateAppDataResult* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(args.userId);
     // Locking is performed depeer in the callstack.
 
     int64_t ceDataInode = -1;
@@ -854,6 +869,10 @@
         const std::vector<android::os::CreateAppDataArgs>& args,
         std::vector<android::os::CreateAppDataResult>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    for (const auto& arg : args) {
+        ENFORCE_VALID_USER(arg.userId);
+    }
+
     // Locking is performed depeer in the callstack.
 
     std::vector<android::os::CreateAppDataResult> results;
@@ -868,6 +887,7 @@
 
 binder::Status InstalldNativeService::reconcileSdkData(
         const android::os::ReconcileSdkDataArgs& args) {
+    ENFORCE_VALID_USER(args.userId);
     // Locking is performed depeer in the callstack.
 
     return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId,
@@ -891,6 +911,7 @@
                                                        int userId, int appId, int previousAppId,
                                                        const std::string& seInfo, int flags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -974,6 +995,7 @@
 binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1041,6 +1063,7 @@
 binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1132,6 +1155,7 @@
 binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags) {
+    ENFORCE_VALID_USER(userId);
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
 
@@ -1218,6 +1242,7 @@
 binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1288,6 +1313,8 @@
 binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags) {
+    ENFORCE_VALID_USER(userId);
+
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     const char* pkgname = packageName.c_str();
 
@@ -1435,6 +1462,7 @@
                                                       int32_t userId, int32_t snapshotId,
                                                       int32_t storageFlags, int64_t* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1569,6 +1597,7 @@
         const int32_t appId, const std::string& seInfo, const int32_t userId,
         const int32_t snapshotId, int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1641,6 +1670,7 @@
         const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId,
         int32_t storageFlags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -1674,6 +1704,7 @@
         const std::optional<std::string>& volumeUuid, const int32_t userId,
         const std::vector<int32_t>& retainSnapshotIds) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
     LOCK_USER();
 
@@ -1864,6 +1895,7 @@
 binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid,
         int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     LOCK_USER();
 
@@ -1884,6 +1916,7 @@
 binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid,
         int32_t userId, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     LOCK_USER();
 
@@ -2671,6 +2704,7 @@
         int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     // NOTE: Locking is relaxed on this method, since it's limited to
     // read-only measurements without mutation.
@@ -2806,6 +2840,7 @@
         int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     // NOTE: Locking is relaxed on this method, since it's limited to
     // read-only measurements without mutation.
@@ -2926,6 +2961,7 @@
         const std::vector<std::string>& packageNames, int32_t userId,
         std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     for (const auto& packageName : packageNames) {
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -2975,6 +3011,7 @@
         const std::optional<std::string>& uuid, int32_t userId,
         std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
 #ifdef ENABLE_STORAGE_CRATES
     LOCK_USER();
@@ -3018,6 +3055,7 @@
 binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid,
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mQuotasLock);
 
@@ -3261,6 +3299,7 @@
         const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
         const std::string& seInfo) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
@@ -3271,6 +3310,7 @@
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags, int32_t appId, const std::string& seInfo) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
 
@@ -3302,6 +3342,7 @@
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags, int32_t appId, const std::string& seInfo) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
 
@@ -3753,6 +3794,7 @@
         int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
         const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     CHECK_ARGUMENT_PATH(codePath);
     LOCK_PACKAGE_USER();
@@ -3775,6 +3817,7 @@
 
 binder::Status InstalldNativeService::cleanupInvalidPackageDirs(
         const std::optional<std::string>& uuid, int32_t userId, int32_t flags) {
+    ENFORCE_VALID_USER(userId);
     const char* uuid_cstr = uuid ? uuid->c_str() : nullptr;
 
     if (flags & FLAG_STORAGE_CE) {
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 ea856c8..a271f0c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -493,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.
  */
@@ -512,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; }
 
@@ -527,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;
@@ -571,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);
@@ -835,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,
@@ -1013,7 +1016,7 @@
     };
 
     Type type;
-    int32_t deviceId;
+    DeviceId deviceId;
     nsecs_t eventTimeNanos;
     uint32_t source;
     int32_t displayId;
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
new file mode 100644
index 0000000..9c0c10e
--- /dev/null
+++ b/include/input/InputEventBuilders.h
@@ -0,0 +1,163 @@
+/*
+ * 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 <android/input.h>
+#include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
+#include <input/Input.h>
+#include <utils/Timers.h> // for nsecs_t, systemTime
+
+#include <vector>
+
+namespace android {
+
+// An arbitrary device id.
+static constexpr uint32_t DEFAULT_DEVICE_ID = 1;
+
+// The default policy flags to use for event injection by tests.
+static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+
+class PointerBuilder {
+public:
+    PointerBuilder(int32_t id, ToolType toolType) {
+        mProperties.clear();
+        mProperties.id = id;
+        mProperties.toolType = toolType;
+        mCoords.clear();
+    }
+
+    PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+    PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+    PointerBuilder& axis(int32_t axis, float value) {
+        mCoords.setAxisValue(axis, value);
+        return *this;
+    }
+
+    PointerProperties buildProperties() const { return mProperties; }
+
+    PointerCoords buildCoords() const { return mCoords; }
+
+private:
+    PointerProperties mProperties;
+    PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+    MotionEventBuilder(int32_t action, int32_t source) {
+        mAction = action;
+        mSource = source;
+        mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mDownTime = mEventTime;
+    }
+
+    MotionEventBuilder& deviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+        return *this;
+    }
+
+    MotionEventBuilder& downTime(nsecs_t downTime) {
+        mDownTime = downTime;
+        return *this;
+    }
+
+    MotionEventBuilder& eventTime(nsecs_t eventTime) {
+        mEventTime = eventTime;
+        return *this;
+    }
+
+    MotionEventBuilder& displayId(int32_t displayId) {
+        mDisplayId = displayId;
+        return *this;
+    }
+
+    MotionEventBuilder& actionButton(int32_t actionButton) {
+        mActionButton = actionButton;
+        return *this;
+    }
+
+    MotionEventBuilder& buttonState(int32_t buttonState) {
+        mButtonState = buttonState;
+        return *this;
+    }
+
+    MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+        mRawXCursorPosition = rawXCursorPosition;
+        return *this;
+    }
+
+    MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+        mRawYCursorPosition = rawYCursorPosition;
+        return *this;
+    }
+
+    MotionEventBuilder& pointer(PointerBuilder pointer) {
+        mPointers.push_back(pointer);
+        return *this;
+    }
+
+    MotionEventBuilder& addFlag(uint32_t flags) {
+        mFlags |= flags;
+        return *this;
+    }
+
+    MotionEvent build() {
+        std::vector<PointerProperties> pointerProperties;
+        std::vector<PointerCoords> pointerCoords;
+        for (const PointerBuilder& pointer : mPointers) {
+            pointerProperties.push_back(pointer.buildProperties());
+            pointerCoords.push_back(pointer.buildCoords());
+        }
+
+        // Set mouse cursor position for the most common cases to avoid boilerplate.
+        if (mSource == AINPUT_SOURCE_MOUSE &&
+            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+            mRawXCursorPosition = pointerCoords[0].getX();
+            mRawYCursorPosition = pointerCoords[0].getY();
+        }
+
+        MotionEvent event;
+        static const ui::Transform kIdentityTransform;
+        event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
+                         mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
+                         MotionClassification::NONE, kIdentityTransform,
+                         /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
+                         mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
+                         mPointers.size(), pointerProperties.data(), pointerCoords.data());
+        return event;
+    }
+
+private:
+    int32_t mAction;
+    int32_t mDeviceId{DEFAULT_DEVICE_ID};
+    int32_t mSource;
+    nsecs_t mDownTime;
+    nsecs_t mEventTime;
+    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+    int32_t mActionButton{0};
+    int32_t mButtonState{0};
+    int32_t mFlags{0};
+    float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+    float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+    std::vector<PointerBuilder> mPointers;
+};
+
+} // namespace android
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 81f68f5..6fda878 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -33,9 +33,9 @@
     pub struct RpcServerRef;
 }
 
-/// SAFETY - The opaque handle can be cloned freely.
+/// SAFETY: The opaque handle can be cloned freely.
 unsafe impl Send for RpcServer {}
-/// SAFETY - The underlying C++ RpcServer class is thread-safe.
+/// SAFETY: The underlying C++ RpcServer class is thread-safe.
 unsafe impl Sync for RpcServer {}
 
 impl RpcServer {
@@ -59,7 +59,10 @@
     /// Creates a binder RPC server, serving the supplied binder service implementation on the given
     /// socket file descriptor. The socket should be bound to an address before calling this
     /// function.
-    pub fn new_bound_socket(mut service: SpIBinder, socket_fd: OwnedFd) -> Result<RpcServer, Error> {
+    pub fn new_bound_socket(
+        mut service: SpIBinder,
+        socket_fd: OwnedFd,
+    ) -> Result<RpcServer, Error> {
         let service = service.as_native_mut();
 
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
@@ -67,7 +70,8 @@
         // The server takes ownership of the socket FD.
         unsafe {
             Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket(
-                service, socket_fd.into_raw_fd(),
+                service,
+                socket_fd.into_raw_fd(),
             ))
         }
     }
@@ -120,7 +124,9 @@
         if ptr.is_null() {
             return Err(Error::new(ErrorKind::Other, "Failed to start server"));
         }
-        Ok(RpcServer::from_ptr(ptr))
+        // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not
+        // null.
+        Ok(unsafe { RpcServer::from_ptr(ptr) })
     }
 }
 
@@ -130,7 +136,7 @@
         &self,
         modes: &[FileDescriptorTransportMode],
     ) {
-        // SAFETY - Does not keep the pointer after returning does, nor does it
+        // SAFETY: Does not keep the pointer after returning does, nor does it
         // read past its boundary. Only passes the 'self' pointer as an opaque handle.
         unsafe {
             binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
@@ -143,18 +149,21 @@
 
     /// Starts a new background thread and calls join(). Returns immediately.
     pub fn start(&self) {
+        // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
         unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
     }
 
     /// Joins the RpcServer thread. The call blocks until the server terminates.
     /// This must be called from exactly one thread.
     pub fn join(&self) {
+        // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
         unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
     }
 
     /// Shuts down the running RpcServer. Can be called multiple times and from
     /// multiple threads. Called automatically during drop().
     pub fn shutdown(&self) -> Result<(), Error> {
+        // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
         if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
             Ok(())
         } else {
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 28c5390..79a9510 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -36,15 +36,15 @@
     pub struct RpcSessionRef;
 }
 
-/// SAFETY - The opaque handle can be cloned freely.
+/// SAFETY: The opaque handle can be cloned freely.
 unsafe impl Send for RpcSession {}
-/// SAFETY - The underlying C++ RpcSession class is thread-safe.
+/// SAFETY: The underlying C++ RpcSession class is thread-safe.
 unsafe impl Sync for RpcSession {}
 
 impl RpcSession {
     /// Allocates a new RpcSession object.
     pub fn new() -> RpcSession {
-        // SAFETY - Takes ownership of the returned handle, which has correct refcount.
+        // SAFETY: Takes ownership of the returned handle, which has correct refcount.
         unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
     }
 }
@@ -58,7 +58,7 @@
 impl RpcSessionRef {
     /// Sets the file descriptor transport mode for this session.
     pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
-        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        // SAFETY: Only passes the 'self' pointer as an opaque handle.
         unsafe {
             binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
                 self.as_ptr(),
@@ -69,7 +69,7 @@
 
     /// Sets the maximum number of incoming threads.
     pub fn set_max_incoming_threads(&self, threads: usize) {
-        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        // SAFETY: Only passes the 'self' pointer as an opaque handle.
         unsafe {
             binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
         };
@@ -77,7 +77,7 @@
 
     /// Sets the maximum number of outgoing connections.
     pub fn set_max_outgoing_connections(&self, connections: usize) {
-        // SAFETY - Only passes the 'self' pointer as an opaque handle.
+        // SAFETY: Only passes the 'self' pointer as an opaque handle.
         unsafe {
             binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
                 self.as_ptr(),
@@ -210,10 +210,10 @@
 type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
 
 unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
+    let request_fd_ptr = param as *mut RequestFd;
     // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
     // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
     // initialized instance.
-    let request_fd_ptr = param as *mut RequestFd;
-    let request_fd = request_fd_ptr.as_mut().unwrap();
+    let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
     request_fd().unwrap_or(-1)
 }
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 993bdca..0bd0781 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -97,8 +97,8 @@
 
 /// Interface stability promise
 ///
-/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
-/// makes no stability guarantees ([`Local`]). [`Local`] is
+/// An interface can promise to be a stable vendor interface ([`Stability::Vintf`]),
+/// or makes no stability guarantees ([`Stability::Local`]). [`Stability::Local`] is
 /// currently the default stability.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
 pub enum Stability {
@@ -139,8 +139,8 @@
 /// via `Binder::new(object)`.
 ///
 /// This is a low-level interface that should normally be automatically
-/// generated from AIDL via the [`declare_binder_interface!`] macro. When using
-/// the AIDL backend, users need only implement the high-level AIDL-defined
+/// generated from AIDL via the [`crate::declare_binder_interface!`] macro.
+/// When using the AIDL backend, users need only implement the high-level AIDL-defined
 /// interface. The AIDL compiler then generates a container struct that wraps
 /// the user-defined service and implements `Remotable`.
 pub trait Remotable: Send + Sync {
@@ -297,18 +297,17 @@
     /// Note: the returned pointer will not be constant. Calling this method
     /// multiple times for the same type will result in distinct class
     /// pointers. A static getter for this value is implemented in
-    /// [`declare_binder_interface!`].
+    /// [`crate::declare_binder_interface!`].
     pub fn new<I: InterfaceClassMethods>() -> InterfaceClass {
         let descriptor = CString::new(I::get_descriptor()).unwrap();
+        // Safety: `AIBinder_Class_define` expects a valid C string, and three
+        // valid callback functions, all non-null pointers. The C string is
+        // copied and need not be valid for longer than the call, so we can drop
+        // it after the call. We can safely assign null to the onDump and
+        // handleShellCommand callbacks as long as the class pointer was
+        // non-null. Rust None for a Option<fn> is guaranteed to be a NULL
+        // pointer. Rust retains ownership of the pointer after it is defined.
         let ptr = unsafe {
-            // Safety: `AIBinder_Class_define` expects a valid C string, and
-            // three valid callback functions, all non-null pointers. The C
-            // string is copied and need not be valid for longer than the call,
-            // so we can drop it after the call. We can safely assign null to
-            // the onDump and handleShellCommand callbacks as long as the class
-            // pointer was non-null. Rust None for a Option<fn> is guaranteed to
-            // be a NULL pointer. Rust retains ownership of the pointer after it
-            // is defined.
             let class = sys::AIBinder_Class_define(
                 descriptor.as_ptr(),
                 Some(I::on_create),
@@ -338,13 +337,12 @@
 
     /// Get the interface descriptor string of this class.
     pub fn get_descriptor(&self) -> String {
+        // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor is
+        // always a two-byte null terminated sequence of u16s. Thus, we can
+        // continue reading from the pointer until we hit a null value, and this
+        // pointer can be a valid slice if the slice length is <= the number of
+        // u16 elements before the null terminator.
         unsafe {
-            // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor
-            // is always a two-byte null terminated sequence of u16s. Thus, we
-            // can continue reading from the pointer until we hit a null value,
-            // and this pointer can be a valid slice if the slice length is <=
-            // the number of u16 elements before the null terminator.
-
             let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0);
             CStr::from_ptr(raw_descriptor)
                 .to_str()
@@ -542,17 +540,15 @@
             static CLASS_INIT: std::sync::Once = std::sync::Once::new();
             static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
+            // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+            // variable, and therefore is thread-safe, as it can only occur
+            // once.
             CLASS_INIT.call_once(|| unsafe {
-                // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
-                // variable, and therefore is thread-safe, as it can only occur
-                // once.
                 CLASS = Some($constructor);
             });
-            unsafe {
-                // Safety: The `CLASS` variable can only be mutated once, above,
-                // and is subsequently safe to read from any thread.
-                CLASS.unwrap()
-            }
+            // Safety: The `CLASS` variable can only be mutated once, above, and
+            // is subsequently safe to read from any thread.
+            unsafe { CLASS.unwrap() }
         }
     };
 }
@@ -664,6 +660,8 @@
     fn as_native_mut(&mut self) -> *mut T;
 }
 
+// Safety: If V is a valid Android C++ type then we can either use that or a
+// null pointer.
 unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
     fn as_native(&self) -> *const T {
         self.as_ref().map_or(ptr::null(), |v| v.as_native())
@@ -924,15 +922,15 @@
                 static CLASS_INIT: std::sync::Once = std::sync::Once::new();
                 static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
+                // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+                // variable, and therefore is thread-safe, as it can only occur
+                // once.
                 CLASS_INIT.call_once(|| unsafe {
-                    // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
-                    // variable, and therefore is thread-safe, as it can only occur
-                    // once.
                     CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
                 });
+                // Safety: The `CLASS` variable can only be mutated once, above,
+                // and is subsequently safe to read from any thread.
                 unsafe {
-                    // Safety: The `CLASS` variable can only be mutated once, above,
-                    // and is subsequently safe to read from any thread.
                     CLASS.unwrap()
                 }
             }
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index ba26062..8d9ce0e 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -112,41 +112,35 @@
 impl Status {
     /// Create a status object representing a successful transaction.
     pub fn ok() -> Self {
-        let ptr = unsafe {
-            // Safety: `AStatus_newOk` always returns a new, heap allocated
-            // pointer to an `ASTatus` object, so we know this pointer will be
-            // valid.
-            //
-            // Rust takes ownership of the returned pointer.
-            sys::AStatus_newOk()
-        };
+        // Safety: `AStatus_newOk` always returns a new, heap allocated
+        // pointer to an `ASTatus` object, so we know this pointer will be
+        // valid.
+        //
+        // Rust takes ownership of the returned pointer.
+        let ptr = unsafe { sys::AStatus_newOk() };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 
     /// Create a status object from a service specific error
     pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
         let ptr = if let Some(message) = message {
-            unsafe {
-                // Safety: Any i32 is a valid service specific error for the
-                // error code parameter. We construct a valid, null-terminated
-                // `CString` from the message, which must be a valid C-style
-                // string to pass as the message. This function always returns a
-                // new, heap allocated pointer to an `AStatus` object, so we
-                // know the returned pointer will be valid.
-                //
-                // Rust takes ownership of the returned pointer.
-                sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
-            }
+            // Safety: Any i32 is a valid service specific error for the
+            // error code parameter. We construct a valid, null-terminated
+            // `CString` from the message, which must be a valid C-style
+            // string to pass as the message. This function always returns a
+            // new, heap allocated pointer to an `AStatus` object, so we
+            // know the returned pointer will be valid.
+            //
+            // Rust takes ownership of the returned pointer.
+            unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) }
         } else {
-            unsafe {
-                // Safety: Any i32 is a valid service specific error for the
-                // error code parameter. This function always returns a new,
-                // heap allocated pointer to an `AStatus` object, so we know the
-                // returned pointer will be valid.
-                //
-                // Rust takes ownership of the returned pointer.
-                sys::AStatus_fromServiceSpecificError(err)
-            }
+            // Safety: Any i32 is a valid service specific error for the
+            // error code parameter. This function always returns a new,
+            // heap allocated pointer to an `AStatus` object, so we know the
+            // returned pointer will be valid.
+            //
+            // Rust takes ownership of the returned pointer.
+            unsafe { sys::AStatus_fromServiceSpecificError(err) }
         };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
@@ -159,6 +153,8 @@
     /// Create a status object from an exception code
     pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
         if let Some(message) = message {
+            // Safety: the C string pointer is valid and not retained by the
+            // function.
             let ptr = unsafe {
                 sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
             };
@@ -187,37 +183,31 @@
 
     /// Returns `true` if this status represents a successful transaction.
     pub fn is_ok(&self) -> bool {
-        unsafe {
-            // Safety: `Status` always contains a valid `AStatus` pointer, so we
-            // are always passing a valid pointer to `AStatus_isOk` here.
-            sys::AStatus_isOk(self.as_native())
-        }
+        // Safety: `Status` always contains a valid `AStatus` pointer, so we
+        // are always passing a valid pointer to `AStatus_isOk` here.
+        unsafe { sys::AStatus_isOk(self.as_native()) }
     }
 
     /// Returns a description of the status.
     pub fn get_description(&self) -> String {
-        let description_ptr = unsafe {
-            // Safety: `Status` always contains a valid `AStatus` pointer, so we
-            // are always passing a valid pointer to `AStatus_getDescription`
-            // here.
-            //
-            // `AStatus_getDescription` always returns a valid pointer to a null
-            // terminated C string. Rust is responsible for freeing this pointer
-            // via `AStatus_deleteDescription`.
-            sys::AStatus_getDescription(self.as_native())
-        };
-        let description = unsafe {
-            // Safety: `AStatus_getDescription` always returns a valid C string,
-            // which can be safely converted to a `CStr`.
-            CStr::from_ptr(description_ptr)
-        };
+        // Safety: `Status` always contains a valid `AStatus` pointer, so we
+        // are always passing a valid pointer to `AStatus_getDescription`
+        // here.
+        //
+        // `AStatus_getDescription` always returns a valid pointer to a null
+        // terminated C string. Rust is responsible for freeing this pointer
+        // via `AStatus_deleteDescription`.
+        let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) };
+        // Safety: `AStatus_getDescription` always returns a valid C string,
+        // which can be safely converted to a `CStr`.
+        let description = unsafe { CStr::from_ptr(description_ptr) };
         let description = description.to_string_lossy().to_string();
+        // Safety: `description_ptr` was returned from
+        // `AStatus_getDescription` above, and must be freed via
+        // `AStatus_deleteDescription`. We must not access the pointer after
+        // this call, so we copy it into an owned string above and return
+        // that string.
         unsafe {
-            // Safety: `description_ptr` was returned from
-            // `AStatus_getDescription` above, and must be freed via
-            // `AStatus_deleteDescription`. We must not access the pointer after
-            // this call, so we copy it into an owned string above and return
-            // that string.
             sys::AStatus_deleteDescription(description_ptr);
         }
         description
@@ -225,12 +215,10 @@
 
     /// Returns the exception code of the status.
     pub fn exception_code(&self) -> ExceptionCode {
-        let code = unsafe {
-            // Safety: `Status` always contains a valid `AStatus` pointer, so we
-            // are always passing a valid pointer to `AStatus_getExceptionCode`
-            // here.
-            sys::AStatus_getExceptionCode(self.as_native())
-        };
+        // Safety: `Status` always contains a valid `AStatus` pointer, so we
+        // are always passing a valid pointer to `AStatus_getExceptionCode`
+        // here.
+        let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) };
         parse_exception_code(code)
     }
 
@@ -241,11 +229,9 @@
     /// exception or a service specific error. To find out if this transaction
     /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
     pub fn transaction_error(&self) -> StatusCode {
-        let code = unsafe {
-            // Safety: `Status` always contains a valid `AStatus` pointer, so we
-            // are always passing a valid pointer to `AStatus_getStatus` here.
-            sys::AStatus_getStatus(self.as_native())
-        };
+        // Safety: `Status` always contains a valid `AStatus` pointer, so we
+        // are always passing a valid pointer to `AStatus_getStatus` here.
+        let code = unsafe { sys::AStatus_getStatus(self.as_native()) };
         parse_status_code(code)
     }
 
@@ -258,12 +244,10 @@
     /// find out if this transaction as a whole is okay, use
     /// [`is_ok`](Self::is_ok) instead.
     pub fn service_specific_error(&self) -> i32 {
-        unsafe {
-            // Safety: `Status` always contains a valid `AStatus` pointer, so we
-            // are always passing a valid pointer to
-            // `AStatus_getServiceSpecificError` here.
-            sys::AStatus_getServiceSpecificError(self.as_native())
-        }
+        // Safety: `Status` always contains a valid `AStatus` pointer, so we
+        // are always passing a valid pointer to
+        // `AStatus_getServiceSpecificError` here.
+        unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) }
     }
 
     /// Calls `op` if the status was ok, otherwise returns an `Err` value of
@@ -321,24 +305,20 @@
 
 impl From<status_t> for Status {
     fn from(status: status_t) -> Status {
-        let ptr = unsafe {
-            // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
-            // this is a safe FFI call. Unknown values will be coerced into
-            // UNKNOWN_ERROR.
-            sys::AStatus_fromStatus(status)
-        };
+        // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
+        // this is a safe FFI call. Unknown values will be coerced into
+        // UNKNOWN_ERROR.
+        let ptr = unsafe { sys::AStatus_fromStatus(status) };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
 
 impl From<ExceptionCode> for Status {
     fn from(code: ExceptionCode) -> Status {
-        let ptr = unsafe {
-            // Safety: `AStatus_fromExceptionCode` expects any
-            // `binder_exception_t` (i32) integer, so this is a safe FFI call.
-            // Unknown values will be coerced into EX_TRANSACTION_FAILED.
-            sys::AStatus_fromExceptionCode(code as i32)
-        };
+        // Safety: `AStatus_fromExceptionCode` expects any
+        // `binder_exception_t` (i32) integer, so this is a safe FFI call.
+        // Unknown values will be coerced into EX_TRANSACTION_FAILED.
+        let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
     }
 }
@@ -363,20 +343,18 @@
 
 impl Drop for Status {
     fn drop(&mut self) {
+        // Safety: `Status` manages the lifetime of its inner `AStatus`
+        // pointee, so we need to delete it here. We know that the pointer
+        // will be valid here since `Status` always contains a valid pointer
+        // while it is alive.
         unsafe {
-            // Safety: `Status` manages the lifetime of its inner `AStatus`
-            // pointee, so we need to delete it here. We know that the pointer
-            // will be valid here since `Status` always contains a valid pointer
-            // while it is alive.
             sys::AStatus_delete(self.0.as_mut());
         }
     }
 }
 
-/// # Safety
-///
-/// `Status` always contains a valid pointer to an `AStatus` object, so we can
-/// trivially convert it to a correctly-typed raw pointer.
+/// Safety: `Status` always contains a valid pointer to an `AStatus` object, so
+/// we can trivially convert it to a correctly-typed raw pointer.
 ///
 /// Care must be taken that the returned pointer is only dereferenced while the
 /// `Status` object is still alive.
@@ -386,11 +364,9 @@
     }
 
     fn as_native_mut(&mut self) -> *mut sys::AStatus {
-        unsafe {
-            // Safety: The pointer will be valid here since `Status` always
-            // contains a valid and initialized pointer while it is alive.
-            self.0.as_mut()
-        }
+        // Safety: The pointer will be valid here since `Status` always contains
+        // a valid and initialized pointer while it is alive.
+        unsafe { self.0.as_mut() }
     }
 }
 
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 5557168..b248f5e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -42,7 +42,7 @@
     rust_object: *mut T,
 }
 
-/// # Safety
+/// Safety:
 ///
 /// A `Binder<T>` is a pair of unique owning pointers to two values:
 ///   * a C++ ABBinder which the C++ API guarantees can be passed between threads
@@ -54,7 +54,7 @@
 /// to how `Box<T>` is `Send` if `T` is `Send`.
 unsafe impl<T: Remotable> Send for Binder<T> {}
 
-/// # Safety
+/// Safety:
 ///
 /// A `Binder<T>` is a pair of unique owning pointers to two values:
 ///   * a C++ ABBinder which is thread-safe, i.e. `Send + Sync`
@@ -89,15 +89,13 @@
     pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
         let class = T::get_class();
         let rust_object = Box::into_raw(Box::new(rust_object));
-        let ibinder = unsafe {
-            // Safety: `AIBinder_new` expects a valid class pointer (which we
-            // initialize via `get_class`), and an arbitrary pointer
-            // argument. The caller owns the returned `AIBinder` pointer, which
-            // is a strong reference to a `BBinder`. This reference should be
-            // decremented via `AIBinder_decStrong` when the reference lifetime
-            // ends.
-            sys::AIBinder_new(class.into(), rust_object as *mut c_void)
-        };
+        // Safety: `AIBinder_new` expects a valid class pointer (which we
+        // initialize via `get_class`), and an arbitrary pointer
+        // argument. The caller owns the returned `AIBinder` pointer, which
+        // is a strong reference to a `BBinder`. This reference should be
+        // decremented via `AIBinder_decStrong` when the reference lifetime
+        // ends.
+        let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
         let mut binder = Binder { ibinder, rust_object };
         binder.mark_stability(stability);
         binder
@@ -176,15 +174,14 @@
     ///        }
     ///        # }
     pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
-        let status = unsafe {
-            // Safety: `AIBinder_setExtension` expects two valid, mutable
-            // `AIBinder` pointers. We are guaranteed that both `self` and
-            // `extension` contain valid `AIBinder` pointers, because they
-            // cannot be initialized without a valid
-            // pointer. `AIBinder_setExtension` does not take ownership of
-            // either parameter.
-            sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut())
-        };
+        let status =
+        // Safety: `AIBinder_setExtension` expects two valid, mutable
+        // `AIBinder` pointers. We are guaranteed that both `self` and
+        // `extension` contain valid `AIBinder` pointers, because they
+        // cannot be initialized without a valid
+        // pointer. `AIBinder_setExtension` does not take ownership of
+        // either parameter.
+            unsafe { sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) };
         status_result(status)
     }
 
@@ -199,9 +196,9 @@
         match stability {
             Stability::Local => self.mark_local_stability(),
             Stability::Vintf => {
+                // Safety: Self always contains a valid `AIBinder` pointer, so
+                // we can always call this C API safely.
                 unsafe {
-                    // Safety: Self always contains a valid `AIBinder` pointer, so
-                    // we can always call this C API safely.
                     sys::AIBinder_markVintfStability(self.as_native_mut());
                 }
             }
@@ -212,9 +209,9 @@
     /// building for android_vendor and system otherwise.
     #[cfg(android_vendor)]
     fn mark_local_stability(&mut self) {
+        // Safety: Self always contains a valid `AIBinder` pointer, so we can
+        // always call this C API safely.
         unsafe {
-            // Safety: Self always contains a valid `AIBinder` pointer, so
-            // we can always call this C API safely.
             sys::AIBinder_markVendorStability(self.as_native_mut());
         }
     }
@@ -223,9 +220,9 @@
     /// building for android_vendor and system otherwise.
     #[cfg(not(android_vendor))]
     fn mark_local_stability(&mut self) {
+        // Safety: Self always contains a valid `AIBinder` pointer, so we can
+        // always call this C API safely.
         unsafe {
-            // Safety: Self always contains a valid `AIBinder` pointer, so
-            // we can always call this C API safely.
             sys::AIBinder_markSystemStability(self.as_native_mut());
         }
     }
@@ -239,13 +236,13 @@
     /// remotable object, which will prevent the object from being dropped while
     /// the `SpIBinder` is alive.
     fn as_binder(&self) -> SpIBinder {
+        // Safety: `self.ibinder` is guaranteed to always be a valid pointer
+        // to an `AIBinder` by the `Binder` constructor. We are creating a
+        // copy of the `self.ibinder` strong reference, but
+        // `SpIBinder::from_raw` assumes it receives an owned pointer with
+        // its own strong reference. We first increment the reference count,
+        // so that the new `SpIBinder` will be tracked as a new reference.
         unsafe {
-            // Safety: `self.ibinder` is guaranteed to always be a valid pointer
-            // to an `AIBinder` by the `Binder` constructor. We are creating a
-            // copy of the `self.ibinder` strong reference, but
-            // `SpIBinder::from_raw` assumes it receives an owned pointer with
-            // its own strong reference. We first increment the reference count,
-            // so that the new `SpIBinder` will be tracked as a new reference.
             sys::AIBinder_incStrong(self.ibinder);
             SpIBinder::from_raw(self.ibinder).unwrap()
         }
@@ -275,10 +272,20 @@
         reply: *mut sys::AParcel,
     ) -> status_t {
         let res = {
-            let mut reply = BorrowedParcel::from_raw(reply).unwrap();
-            let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap();
-            let object = sys::AIBinder_getUserData(binder);
-            let binder: &T = &*(object as *const T);
+            // Safety: The caller must give us a parcel pointer which is either
+            // null or valid at least for the duration of this function call. We
+            // don't keep the resulting value beyond the function.
+            let mut reply = unsafe { BorrowedParcel::from_raw(reply).unwrap() };
+            // Safety: The caller must give us a parcel pointer which is either
+            // null or valid at least for the duration of this function call. We
+            // don't keep the resulting value beyond the function.
+            let data = unsafe { BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap() };
+            // Safety: Our caller promised that `binder` is a non-null, valid
+            // pointer to a local `AIBinder`.
+            let object = unsafe { sys::AIBinder_getUserData(binder) };
+            // Safety: Our caller promised that the binder has a `T` pointer in
+            // its user data.
+            let binder: &T = unsafe { &*(object as *const T) };
             binder.on_transact(code, &data, &mut reply)
         };
         match res {
@@ -295,7 +302,9 @@
     /// Must be called with a valid pointer to a `T` object. After this call,
     /// the pointer will be invalid and should not be dereferenced.
     unsafe extern "C" fn on_destroy(object: *mut c_void) {
-        drop(Box::from_raw(object as *mut T));
+        // Safety: Our caller promised that `object` is a valid pointer to a
+        // `T`.
+        drop(unsafe { Box::from_raw(object as *mut T) });
     }
 
     /// Called whenever a new, local `AIBinder` object is needed of a specific
@@ -320,7 +329,7 @@
     /// Must be called with a non-null, valid pointer to a local `AIBinder` that
     /// contains a `T` pointer in its user data. fd should be a non-owned file
     /// descriptor, and args must be an array of null-terminated string
-    /// poiinters with length num_args.
+    /// pointers with length num_args.
     unsafe extern "C" fn on_dump(
         binder: *mut sys::AIBinder,
         fd: i32,
@@ -330,8 +339,9 @@
         if fd < 0 {
             return StatusCode::UNEXPECTED_NULL as status_t;
         }
-        // We don't own this file, so we need to be careful not to drop it.
-        let file = ManuallyDrop::new(File::from_raw_fd(fd));
+        // Safety: Our caller promised that fd is a file descriptor. We don't
+        // own this file descriptor, so we need to be careful not to drop it.
+        let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
 
         if args.is_null() && num_args != 0 {
             return StatusCode::UNEXPECTED_NULL as status_t;
@@ -340,14 +350,22 @@
         let args = if args.is_null() || num_args == 0 {
             vec![]
         } else {
-            slice::from_raw_parts(args, num_args as usize)
-                .iter()
-                .map(|s| CStr::from_ptr(*s))
-                .collect()
+            // Safety: Our caller promised that `args` is an array of
+            // null-terminated string pointers with length `num_args`.
+            unsafe {
+                slice::from_raw_parts(args, num_args as usize)
+                    .iter()
+                    .map(|s| CStr::from_ptr(*s))
+                    .collect()
+            }
         };
 
-        let object = sys::AIBinder_getUserData(binder);
-        let binder: &T = &*(object as *const T);
+        // Safety: Our caller promised that `binder` is a non-null, valid
+        // pointer to a local `AIBinder`.
+        let object = unsafe { sys::AIBinder_getUserData(binder) };
+        // Safety: Our caller promised that the binder has a `T` pointer in its
+        // user data.
+        let binder: &T = unsafe { &*(object as *const T) };
         let res = binder.on_dump(&file, &args);
 
         match res {
@@ -363,11 +381,11 @@
     // actually destroys the object, it calls `on_destroy` and we can drop the
     // `rust_object` then.
     fn drop(&mut self) {
+        // Safety: When `self` is dropped, we can no longer access the
+        // reference, so can decrement the reference count. `self.ibinder` is
+        // always a valid `AIBinder` pointer, so is valid to pass to
+        // `AIBinder_decStrong`.
         unsafe {
-            // Safety: When `self` is dropped, we can no longer access the
-            // reference, so can decrement the reference count. `self.ibinder`
-            // is always a valid `AIBinder` pointer, so is valid to pass to
-            // `AIBinder_decStrong`.
             sys::AIBinder_decStrong(self.ibinder);
         }
     }
@@ -377,14 +395,11 @@
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
-        unsafe {
-            // Safety: While `self` is alive, the reference count of the
-            // underlying object is > 0 and therefore `on_destroy` cannot be
-            // called. Therefore while `self` is alive, we know that
-            // `rust_object` is still a valid pointer to a heap allocated object
-            // of type `T`.
-            &*self.rust_object
-        }
+        // Safety: While `self` is alive, the reference count of the underlying
+        // object is > 0 and therefore `on_destroy` cannot be called. Therefore
+        // while `self` is alive, we know that `rust_object` is still a valid
+        // pointer to a heap allocated object of type `T`.
+        unsafe { &*self.rust_object }
     }
 }
 
@@ -405,13 +420,10 @@
         if Some(class) != ibinder.get_class() {
             return Err(StatusCode::BAD_TYPE);
         }
-        let userdata = unsafe {
-            // Safety: `SpIBinder` always holds a valid pointer pointer to an
-            // `AIBinder`, which we can safely pass to
-            // `AIBinder_getUserData`. `ibinder` retains ownership of the
-            // returned pointer.
-            sys::AIBinder_getUserData(ibinder.as_native_mut())
-        };
+        // Safety: `SpIBinder` always holds a valid pointer pointer to an
+        // `AIBinder`, which we can safely pass to `AIBinder_getUserData`.
+        // `ibinder` retains ownership of the returned pointer.
+        let userdata = unsafe { sys::AIBinder_getUserData(ibinder.as_native_mut()) };
         if userdata.is_null() {
             return Err(StatusCode::UNEXPECTED_NULL);
         }
@@ -422,12 +434,10 @@
     }
 }
 
-/// # Safety
-///
-/// The constructor for `Binder` guarantees that `self.ibinder` will contain a
-/// valid, non-null pointer to an `AIBinder`, so this implementation is type
-/// safe. `self.ibinder` will remain valid for the entire lifetime of `self`
-/// because we hold a strong reference to the `AIBinder` until `self` is
+/// Safety: The constructor for `Binder` guarantees that `self.ibinder` will
+/// contain a valid, non-null pointer to an `AIBinder`, so this implementation
+/// is type safe. `self.ibinder` will remain valid for the entire lifetime of
+/// `self` because we hold a strong reference to the `AIBinder` until `self` is
 /// dropped.
 unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
     fn as_native(&self) -> *const sys::AIBinder {
@@ -447,14 +457,12 @@
 /// This function will panic if the identifier contains a 0 byte (NUL).
 pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
     let instance = CString::new(identifier).unwrap();
-    let status = unsafe {
-        // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
-        // string pointers. Caller retains ownership of both
-        // pointers. `AServiceManager_addService` creates a new strong reference
-        // and copies the string, so both pointers need only be valid until the
-        // call returns.
-        sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr())
-    };
+    let status =
+    // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+    // string pointers. Caller retains ownership of both pointers.
+    // `AServiceManager_addService` creates a new strong reference and copies
+    // the string, so both pointers need only be valid until the call returns.
+        unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
     status_result(status)
 }
 
@@ -470,13 +478,12 @@
 /// This function will panic if the identifier contains a 0 byte (NUL).
 pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
     let instance = CString::new(identifier).unwrap();
+    // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+    // string pointers. Caller retains ownership of both
+    // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+    // and copies the string, so both pointers need only be valid until the
+    // call returns.
     let status = unsafe {
-        // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
-        // string pointers. Caller retains ownership of both
-        // pointers. `AServiceManager_registerLazyService` creates a new strong reference
-        // and copies the string, so both pointers need only be valid until the
-        // call returns.
-
         sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
     };
     status_result(status)
@@ -491,10 +498,8 @@
 ///
 /// Consider using [`LazyServiceGuard`] rather than calling this directly.
 pub fn force_lazy_services_persist(persist: bool) {
-    unsafe {
-        // Safety: No borrowing or transfer of ownership occurs here.
-        sys::AServiceManager_forceLazyServicesPersist(persist)
-    }
+    // Safety: No borrowing or transfer of ownership occurs here.
+    unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
 }
 
 /// An RAII object to ensure a process which registers lazy services is not killed. During the
@@ -576,8 +581,6 @@
 /// Determine whether the current thread is currently executing an incoming
 /// transaction.
 pub fn is_handling_transaction() -> bool {
-    unsafe {
-        // Safety: This method is always safe to call.
-        sys::AIBinder_isHandlingTransaction()
-    }
+    // Safety: This method is always safe to call.
+    unsafe { sys::AIBinder_isHandlingTransaction() }
 }
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index e4c568e..0b0f9f9 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -52,11 +52,8 @@
     ptr: NonNull<sys::AParcel>,
 }
 
-/// # Safety
-///
-/// This type guarantees that it owns the AParcel and that all access to
-/// the AParcel happens through the Parcel, so it is ok to send across
-/// threads.
+/// Safety: This type guarantees that it owns the AParcel and that all access to
+/// the AParcel happens through the Parcel, so it is ok to send across threads.
 unsafe impl Send for Parcel {}
 
 /// Container for a message (data and object references) that can be sent
@@ -73,11 +70,9 @@
 impl Parcel {
     /// Create a new empty `Parcel`.
     pub fn new() -> Parcel {
-        let ptr = unsafe {
-            // Safety: If `AParcel_create` succeeds, it always returns
-            // a valid pointer. If it fails, the process will crash.
-            sys::AParcel_create()
-        };
+        // Safety: If `AParcel_create` succeeds, it always returns
+        // a valid pointer. If it fails, the process will crash.
+        let ptr = unsafe { sys::AParcel_create() };
         Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") }
     }
 
@@ -171,10 +166,8 @@
     }
 }
 
-/// # Safety
-///
-/// The `Parcel` constructors guarantee that a `Parcel` object will always
-/// contain a valid pointer to an `AParcel`.
+/// Safety: The `Parcel` constructors guarantee that a `Parcel` object will
+/// always contain a valid pointer to an `AParcel`.
 unsafe impl AsNative<sys::AParcel> for Parcel {
     fn as_native(&self) -> *const sys::AParcel {
         self.ptr.as_ptr()
@@ -185,10 +178,8 @@
     }
 }
 
-/// # Safety
-///
-/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object
-/// will always contain a valid pointer to an `AParcel`.
+/// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel`
+/// object will always contain a valid pointer to an `AParcel`.
 unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
     fn as_native(&self) -> *const sys::AParcel {
         self.ptr.as_ptr()
@@ -203,10 +194,8 @@
 impl<'a> BorrowedParcel<'a> {
     /// Data written to parcelable is zero'd before being deleted or reallocated.
     pub fn mark_sensitive(&mut self) {
-        unsafe {
-            // Safety: guaranteed to have a parcel object, and this method never fails
-            sys::AParcel_markSensitive(self.as_native())
-        }
+        // Safety: guaranteed to have a parcel object, and this method never fails
+        unsafe { sys::AParcel_markSensitive(self.as_native()) }
     }
 
     /// Write a type that implements [`Serialize`] to the parcel.
@@ -265,11 +254,15 @@
             f(&mut subparcel)?;
         }
         let end = self.get_data_position();
+        // Safety: start is less than the current size of the parcel data
+        // buffer, because we just got it with `get_data_position`.
         unsafe {
             self.set_data_position(start)?;
         }
         assert!(end >= start);
         self.write(&(end - start))?;
+        // Safety: end is less than the current size of the parcel data
+        // buffer, because we just got it with `get_data_position`.
         unsafe {
             self.set_data_position(end)?;
         }
@@ -278,20 +271,16 @@
 
     /// Returns the current position in the parcel data.
     pub fn get_data_position(&self) -> i32 {
-        unsafe {
-            // Safety: `BorrowedParcel` always contains a valid pointer to an
-            // `AParcel`, and this call is otherwise safe.
-            sys::AParcel_getDataPosition(self.as_native())
-        }
+        // Safety: `BorrowedParcel` always contains a valid pointer to an
+        // `AParcel`, and this call is otherwise safe.
+        unsafe { sys::AParcel_getDataPosition(self.as_native()) }
     }
 
     /// Returns the total size of the parcel.
     pub fn get_data_size(&self) -> i32 {
-        unsafe {
-            // Safety: `BorrowedParcel` always contains a valid pointer to an
-            // `AParcel`, and this call is otherwise safe.
-            sys::AParcel_getDataSize(self.as_native())
-        }
+        // Safety: `BorrowedParcel` always contains a valid pointer to an
+        // `AParcel`, and this call is otherwise safe.
+        unsafe { sys::AParcel_getDataSize(self.as_native()) }
     }
 
     /// Move the current read/write position in the parcel.
@@ -304,7 +293,9 @@
     /// accesses are bounds checked, this call is still safe, but we can't rely
     /// on that.
     pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
-        status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
+        // Safety: `BorrowedParcel` always contains a valid pointer to an
+        // `AParcel`, and the caller guarantees that `pos` is within bounds.
+        status_result(unsafe { sys::AParcel_setDataPosition(self.as_native(), pos) })
     }
 
     /// Append a subset of another parcel.
@@ -317,10 +308,10 @@
         start: i32,
         size: i32,
     ) -> Result<()> {
+        // Safety: `Parcel::appendFrom` from C++ checks that `start`
+        // and `size` are in bounds, and returns an error otherwise.
+        // Both `self` and `other` always contain valid pointers.
         let status = unsafe {
-            // Safety: `Parcel::appendFrom` from C++ checks that `start`
-            // and `size` are in bounds, and returns an error otherwise.
-            // Both `self` and `other` always contain valid pointers.
             sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size)
         };
         status_result(status)
@@ -418,7 +409,9 @@
     /// accesses are bounds checked, this call is still safe, but we can't rely
     /// on that.
     pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
-        self.borrowed_ref().set_data_position(pos)
+        // Safety: We have the same safety requirements as
+        // `BorrowedParcel::set_data_position`.
+        unsafe { self.borrowed_ref().set_data_position(pos) }
     }
 
     /// Append a subset of another parcel.
@@ -461,7 +454,7 @@
     /// and call a closure with the sub-parcel as its parameter.
     /// The closure can keep reading data from the sub-parcel
     /// until it runs out of input data. The closure is responsible
-    /// for calling [`ReadableSubParcel::has_more_data`] to check for
+    /// for calling `ReadableSubParcel::has_more_data` to check for
     /// more data before every read, at least until Rust generators
     /// are stabilized.
     /// After the closure returns, skip to the end of the current
@@ -504,7 +497,10 @@
         f(subparcel)?;
 
         // Advance the data position to the actual end,
-        // in case the closure read less data than was available
+        // in case the closure read less data than was available.
+        //
+        // Safety: end must be less than the current size of the parcel, because
+        // we checked above against `get_data_size`.
         unsafe {
             self.set_data_position(end)?;
         }
@@ -595,7 +591,7 @@
     /// and call a closure with the sub-parcel as its parameter.
     /// The closure can keep reading data from the sub-parcel
     /// until it runs out of input data. The closure is responsible
-    /// for calling [`ReadableSubParcel::has_more_data`] to check for
+    /// for calling `ReadableSubParcel::has_more_data` to check for
     /// more data before every read, at least until Rust generators
     /// are stabilized.
     /// After the closure returns, skip to the end of the current
@@ -649,17 +645,17 @@
 // Internal APIs
 impl<'a> BorrowedParcel<'a> {
     pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
+        // Safety: `BorrowedParcel` always contains a valid pointer to an
+        // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
+        // null or a valid pointer to an `AIBinder`, both of which are
+        // valid, safe inputs to `AParcel_writeStrongBinder`.
+        //
+        // This call does not take ownership of the binder. However, it does
+        // require a mutable pointer, which we cannot extract from an
+        // immutable reference, so we clone the binder, incrementing the
+        // refcount before the call. The refcount will be immediately
+        // decremented when this temporary is dropped.
         unsafe {
-            // Safety: `BorrowedParcel` always contains a valid pointer to an
-            // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
-            // null or a valid pointer to an `AIBinder`, both of which are
-            // valid, safe inputs to `AParcel_writeStrongBinder`.
-            //
-            // This call does not take ownership of the binder. However, it does
-            // require a mutable pointer, which we cannot extract from an
-            // immutable reference, so we clone the binder, incrementing the
-            // refcount before the call. The refcount will be immediately
-            // decremented when this temporary is dropped.
             status_result(sys::AParcel_writeStrongBinder(
                 self.as_native_mut(),
                 binder.cloned().as_native_mut(),
@@ -669,33 +665,28 @@
 
     pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
         let mut binder = ptr::null_mut();
-        let status = unsafe {
-            // Safety: `BorrowedParcel` always contains a valid pointer to an
-            // `AParcel`. We pass a valid, mutable out pointer to the `binder`
-            // parameter. After this call, `binder` will be either null or a
-            // valid pointer to an `AIBinder` owned by the caller.
-            sys::AParcel_readStrongBinder(self.as_native(), &mut binder)
-        };
+        // Safety: `BorrowedParcel` always contains a valid pointer to an
+        // `AParcel`. We pass a valid, mutable out pointer to the `binder`
+        // parameter. After this call, `binder` will be either null or a
+        // valid pointer to an `AIBinder` owned by the caller.
+        let status = unsafe { sys::AParcel_readStrongBinder(self.as_native(), &mut binder) };
 
         status_result(status)?;
 
-        Ok(unsafe {
-            // Safety: `binder` is either null or a valid, owned pointer at this
-            // point, so can be safely passed to `SpIBinder::from_raw`.
-            SpIBinder::from_raw(binder)
-        })
+        // Safety: `binder` is either null or a valid, owned pointer at this
+        // point, so can be safely passed to `SpIBinder::from_raw`.
+        Ok(unsafe { SpIBinder::from_raw(binder) })
     }
 }
 
 impl Drop for Parcel {
     fn drop(&mut self) {
         // Run the C++ Parcel complete object destructor
-        unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. Since we own the parcel, we can safely delete it
-            // here.
-            sys::AParcel_delete(self.ptr.as_ptr())
-        }
+        //
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. Since we own the parcel, we can safely delete it
+        // here.
+        unsafe { sys::AParcel_delete(self.ptr.as_ptr()) }
     }
 }
 
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 7fe37f3..5c688fa 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -73,14 +73,12 @@
 impl Serialize for ParcelFileDescriptor {
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
         let fd = self.0.as_raw_fd();
-        let status = unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
-            // valid file, so we can borrow a valid file
-            // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
-            // ownership of the fd, so we need not duplicate it first.
-            sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd)
-        };
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
+        // valid file, so we can borrow a valid file
+        // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
+        // ownership of the fd, so we need not duplicate it first.
+        let status = unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) };
         status_result(status)
     }
 }
@@ -92,13 +90,12 @@
         if let Some(f) = this {
             f.serialize(parcel)
         } else {
-            let status = unsafe {
-                // Safety: `Parcel` always contains a valid pointer to an
-                // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
-                // value `-1` as the file descriptor to signify serializing a
-                // null file descriptor.
-                sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32)
-            };
+            let status =
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
+            // value `-1` as the file descriptor to signify serializing a
+            // null file descriptor.
+                unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) };
             status_result(status)
         }
     }
@@ -107,25 +104,23 @@
 impl DeserializeOption for ParcelFileDescriptor {
     fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
         let mut fd = -1i32;
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. We pass a valid mutable pointer to an i32, which
+        // `AParcel_readParcelFileDescriptor` assigns the valid file
+        // descriptor into, or `-1` if deserializing a null file
+        // descriptor. The read function passes ownership of the file
+        // descriptor to its caller if it was non-null, so we must take
+        // ownership of the file and ensure that it is eventually closed.
         unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. We pass a valid mutable pointer to an i32, which
-            // `AParcel_readParcelFileDescriptor` assigns the valid file
-            // descriptor into, or `-1` if deserializing a null file
-            // descriptor. The read function passes ownership of the file
-            // descriptor to its caller if it was non-null, so we must take
-            // ownership of the file and ensure that it is eventually closed.
             status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?;
         }
         if fd < 0 {
             Ok(None)
         } else {
-            let file = unsafe {
-                // Safety: At this point, we know that the file descriptor was
-                // not -1, so must be a valid, owned file descriptor which we
-                // can safely turn into a `File`.
-                File::from_raw_fd(fd)
-            };
+            // Safety: At this point, we know that the file descriptor was
+            // not -1, so must be a valid, owned file descriptor which we
+            // can safely turn into a `File`.
+            let file = unsafe { File::from_raw_fd(fd) };
             Ok(Some(ParcelFileDescriptor::new(file)))
         }
     }
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 5d8c11c..038f198 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -50,14 +50,14 @@
     fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>;
 }
 
-/// A struct whose instances can be written to a [`Parcel`].
+/// A struct whose instances can be written to a [`crate::parcel::Parcel`].
 // Might be able to hook this up as a serde backend in the future?
 pub trait Serialize {
-    /// Serialize this instance into the given [`Parcel`].
+    /// Serialize this instance into the given [`crate::parcel::Parcel`].
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
 }
 
-/// A struct whose instances can be restored from a [`Parcel`].
+/// A struct whose instances can be restored from a [`crate::parcel::Parcel`].
 // Might be able to hook this up as a serde backend in the future?
 pub trait Deserialize: Sized {
     /// Type for the uninitialized value of this type. Will be either `Self`
@@ -80,10 +80,10 @@
     /// Convert an initialized value of type `Self` into `Self::UninitType`.
     fn from_init(value: Self) -> Self::UninitType;
 
-    /// Deserialize an instance from the given [`Parcel`].
+    /// Deserialize an instance from the given [`crate::parcel::Parcel`].
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
 
-    /// Deserialize an instance from the given [`Parcel`] onto the
+    /// Deserialize an instance from the given [`crate::parcel::Parcel`] onto the
     /// current object. This operation will overwrite the old value
     /// partially or completely, depending on how much data is available.
     fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
@@ -102,8 +102,8 @@
 pub trait SerializeArray: Serialize + Sized {
     /// Serialize an array of this type into the given parcel.
     fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        // Safety: Safe FFI, slice will always be a safe pointer to pass.
         let res = unsafe {
-            // Safety: Safe FFI, slice will always be a safe pointer to pass.
             sys::AParcel_writeParcelableArray(
                 parcel.as_native_mut(),
                 slice.as_ptr() as *const c_void,
@@ -117,7 +117,9 @@
 
 /// Callback to serialize an element of a generic parcelable array.
 ///
-/// Safety: We are relying on binder_ndk to not overrun our slice. As long as it
+/// # Safety
+///
+/// We are relying on binder_ndk to not overrun our slice. As long as it
 /// doesn't provide an index larger than the length of the original slice in
 /// serialize_array, this operation is safe. The index provided is zero-based.
 unsafe extern "C" fn serialize_element<T: Serialize>(
@@ -125,9 +127,14 @@
     array: *const c_void,
     index: usize,
 ) -> status_t {
-    let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1);
+    // Safety: The caller guarantees that `array` is a valid pointer of the
+    // appropriate type.
+    let slice: &[T] = unsafe { slice::from_raw_parts(array.cast(), index + 1) };
 
-    let mut parcel = match BorrowedParcel::from_raw(parcel) {
+    // Safety: The caller must give us a parcel pointer which is either null or
+    // valid at least for the duration of this function call. We don't keep the
+    // resulting value beyond the function.
+    let mut parcel = match unsafe { BorrowedParcel::from_raw(parcel) } {
         None => return StatusCode::UNEXPECTED_NULL as status_t,
         Some(p) => p,
     };
@@ -142,9 +149,9 @@
     /// Deserialize an array of type from the given parcel.
     fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
         let mut vec: Option<Vec<Self::UninitType>> = None;
+        // Safety: Safe FFI, vec is the correct opaque type expected by
+        // allocate_vec and deserialize_element.
         let res = unsafe {
-            // Safety: Safe FFI, vec is the correct opaque type expected by
-            // allocate_vec and deserialize_element.
             sys::AParcel_readParcelableArray(
                 parcel.as_native(),
                 &mut vec as *mut _ as *mut c_void,
@@ -153,21 +160,21 @@
             )
         };
         status_result(res)?;
-        let vec: Option<Vec<Self>> = unsafe {
-            // Safety: We are assuming that the NDK correctly initialized every
-            // element of the vector by now, so we know that all the
-            // UninitTypes are now properly initialized. We can transmute from
-            // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
-            // alignment and size as T, so the pointer to the vector allocation
-            // will be compatible.
-            mem::transmute(vec)
-        };
+        // Safety: We are assuming that the NDK correctly initialized every
+        // element of the vector by now, so we know that all the
+        // UninitTypes are now properly initialized. We can transmute from
+        // Vec<T::UninitType> to Vec<T> because T::UninitType has the same
+        // alignment and size as T, so the pointer to the vector allocation
+        // will be compatible.
+        let vec: Option<Vec<Self>> = unsafe { mem::transmute(vec) };
         Ok(vec)
     }
 }
 
 /// Callback to deserialize a parcelable element.
 ///
+/// # Safety
+///
 /// The opaque array data pointer must be a mutable pointer to an
 /// `Option<Vec<T::UninitType>>` with at least enough elements for `index` to be valid
 /// (zero-based).
@@ -176,13 +183,18 @@
     array: *mut c_void,
     index: usize,
 ) -> status_t {
-    let vec = &mut *(array as *mut Option<Vec<T::UninitType>>);
+    // Safety: The caller guarantees that `array` is a valid pointer of the
+    // appropriate type.
+    let vec = unsafe { &mut *(array as *mut Option<Vec<T::UninitType>>) };
     let vec = match vec {
         Some(v) => v,
         None => return StatusCode::BAD_INDEX as status_t,
     };
 
-    let parcel = match BorrowedParcel::from_raw(parcel as *mut _) {
+    // Safety: The caller must give us a parcel pointer which is either null or
+    // valid at least for the duration of this function call. We don't keep the
+    // resulting value beyond the function.
+    let parcel = match unsafe { BorrowedParcel::from_raw(parcel as *mut _) } {
         None => return StatusCode::UNEXPECTED_NULL as status_t,
         Some(p) => p,
     };
@@ -254,16 +266,21 @@
 ///
 /// The opaque data pointer passed to the array read function must be a mutable
 /// pointer to an `Option<Vec<T::UninitType>>`. `buffer` will be assigned a mutable pointer
-/// to the allocated vector data if this function returns true.
+/// to the allocated vector data if this function returns true. `buffer` must be a valid pointer.
 unsafe extern "C" fn allocate_vec_with_buffer<T: Deserialize>(
     data: *mut c_void,
     len: i32,
     buffer: *mut *mut T,
 ) -> bool {
-    let res = allocate_vec::<T>(data, len);
-    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
+    // Safety: We have the same safety requirements as `allocate_vec` for `data`.
+    let res = unsafe { allocate_vec::<T>(data, len) };
+    // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type.
+    let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) };
     if let Some(new_vec) = vec {
-        *buffer = new_vec.as_mut_ptr() as *mut T;
+        // Safety: The caller guarantees that `buffer` is a valid pointer.
+        unsafe {
+            *buffer = new_vec.as_mut_ptr() as *mut T;
+        }
     }
     res
 }
@@ -275,7 +292,8 @@
 /// The opaque data pointer passed to the array read function must be a mutable
 /// pointer to an `Option<Vec<T::UninitType>>`.
 unsafe extern "C" fn allocate_vec<T: Deserialize>(data: *mut c_void, len: i32) -> bool {
-    let vec = &mut *(data as *mut Option<Vec<T::UninitType>>);
+    // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type.
+    let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) };
     if len < 0 {
         *vec = None;
         return true;
@@ -286,7 +304,10 @@
     let mut new_vec: Vec<T::UninitType> = Vec::with_capacity(len as usize);
     new_vec.resize_with(len as usize, T::uninit);
 
-    ptr::write(vec, Some(new_vec));
+    // Safety: The caller guarantees that vec is a valid mutable pointer to the appropriate type.
+    unsafe {
+        ptr::write(vec, Some(new_vec));
+    }
     true
 }
 
@@ -305,21 +326,21 @@
     // Assert at compile time that `T` and `T::UninitType` have the same size and alignment.
     let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT;
 
-    // We can convert from Vec<T::UninitType> to Vec<T> because T::UninitType
-    // has the same alignment and size as T, so the pointer to the vector
-    // allocation will be compatible.
     let mut vec = ManuallyDrop::new(vec);
-    Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity())
+    // Safety: We can convert from Vec<T::UninitType> to Vec<T> because
+    // T::UninitType has the same alignment and size as T, so the pointer to the
+    // vector allocation will be compatible.
+    unsafe { Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) }
 }
 
 macro_rules! impl_parcelable {
     {Serialize, $ty:ty, $write_fn:path} => {
         impl Serialize for $ty {
             fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`, and any `$ty` literal value is safe to pass to
+                // `$write_fn`.
                 unsafe {
-                    // Safety: `Parcel` always contains a valid pointer to an
-                    // `AParcel`, and any `$ty` literal value is safe to pass to
-                    // `$write_fn`.
                     status_result($write_fn(parcel.as_native_mut(), *self))
                 }
             }
@@ -333,11 +354,11 @@
             fn from_init(value: Self) -> Self::UninitType { value }
             fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
                 let mut val = Self::default();
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. We pass a valid, mutable pointer to `val`, a
+                // literal of type `$ty`, and `$read_fn` will write the
+                // value read into `val` if successful
                 unsafe {
-                    // Safety: `Parcel` always contains a valid pointer to an
-                    // `AParcel`. We pass a valid, mutable pointer to `val`, a
-                    // literal of type `$ty`, and `$read_fn` will write the
-                    // value read into `val` if successful
                     status_result($read_fn(parcel.as_native(), &mut val))?
                 };
                 Ok(val)
@@ -348,13 +369,13 @@
     {SerializeArray, $ty:ty, $write_array_fn:path} => {
         impl SerializeArray for $ty {
             fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
+                // will be a valid pointer to an array of elements of type
+                // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
+                // dangling, but this is safe since the pointer is not
+                // dereferenced if the length parameter is 0.
                 let status = unsafe {
-                    // Safety: `Parcel` always contains a valid pointer to an
-                    // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
-                    // will be a valid pointer to an array of elements of type
-                    // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
-                    // dangling, but this is safe since the pointer is not
-                    // dereferenced if the length parameter is 0.
                     $write_array_fn(
                         parcel.as_native_mut(),
                         slice.as_ptr(),
@@ -373,11 +394,11 @@
         impl DeserializeArray for $ty {
             fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
                 let mut vec: Option<Vec<Self::UninitType>> = None;
+                // Safety: `Parcel` always contains a valid pointer to an
+                // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
+                // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
+                // correct for it.
                 let status = unsafe {
-                    // Safety: `Parcel` always contains a valid pointer to an
-                    // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
-                    // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is
-                    // correct for it.
                     $read_array_fn(
                         parcel.as_native(),
                         &mut vec as *mut _ as *mut c_void,
@@ -385,11 +406,11 @@
                     )
                 };
                 status_result(status)?;
+                // Safety: We are assuming that the NDK correctly
+                // initialized every element of the vector by now, so we
+                // know that all the UninitTypes are now properly
+                // initialized.
                 let vec: Option<Vec<Self>> = unsafe {
-                    // Safety: We are assuming that the NDK correctly
-                    // initialized every element of the vector by now, so we
-                    // know that all the UninitTypes are now properly
-                    // initialized.
                     vec.map(|vec| vec_assume_init(vec))
                 };
                 Ok(vec)
@@ -479,13 +500,13 @@
 
 impl SerializeArray for u8 {
     fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+        // valid pointer to an array of elements of type `$ty`. If the slice
+        // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+        // since the pointer is not dereferenced if the length parameter is
+        // 0.
         let status = unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
-            // valid pointer to an array of elements of type `$ty`. If the slice
-            // length is 0, `slice.as_ptr()` may be dangling, but this is safe
-            // since the pointer is not dereferenced if the length parameter is
-            // 0.
             sys::AParcel_writeByteArray(
                 parcel.as_native_mut(),
                 slice.as_ptr() as *const i8,
@@ -518,13 +539,13 @@
 
 impl SerializeArray for i16 {
     fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+        // valid pointer to an array of elements of type `$ty`. If the slice
+        // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+        // since the pointer is not dereferenced if the length parameter is
+        // 0.
         let status = unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
-            // valid pointer to an array of elements of type `$ty`. If the slice
-            // length is 0, `slice.as_ptr()` may be dangling, but this is safe
-            // since the pointer is not dereferenced if the length parameter is
-            // 0.
             sys::AParcel_writeCharArray(
                 parcel.as_native_mut(),
                 slice.as_ptr() as *const u16,
@@ -538,22 +559,22 @@
 impl SerializeOption for str {
     fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
         match this {
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. If the string pointer is null,
+            // `AParcel_writeString` requires that the length is -1 to
+            // indicate that we want to serialize a null string.
             None => unsafe {
-                // Safety: `Parcel` always contains a valid pointer to an
-                // `AParcel`. If the string pointer is null,
-                // `AParcel_writeString` requires that the length is -1 to
-                // indicate that we want to serialize a null string.
                 status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1))
             },
+            // Safety: `Parcel` always contains a valid pointer to an
+            // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
+            // string pointer of `length` bytes, which is what str in Rust
+            // is. The docstring for `AParcel_writeString` says that the
+            // string input should be null-terminated, but it doesn't
+            // actually rely on that fact in the code. If this ever becomes
+            // necessary, we will need to null-terminate the str buffer
+            // before sending it.
             Some(s) => unsafe {
-                // Safety: `Parcel` always contains a valid pointer to an
-                // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
-                // string pointer of `length` bytes, which is what str in Rust
-                // is. The docstring for `AParcel_writeString` says that the
-                // string input should be null-terminated, but it doesn't
-                // actually rely on that fact in the code. If this ever becomes
-                // necessary, we will need to null-terminate the str buffer
-                // before sending it.
                 status_result(sys::AParcel_writeString(
                     parcel.as_native_mut(),
                     s.as_ptr() as *const c_char,
@@ -597,11 +618,11 @@
 
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut vec: Option<Vec<u8>> = None;
+        // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
+        // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
+        // for `allocate_vec`, so `vec` is safe to pass as the opaque data
+        // pointer on platforms where char is signed.
         let status = unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
-            // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
-            // for `allocate_vec`, so `vec` is safe to pass as the opaque data
-            // pointer on platforms where char is signed.
             sys::AParcel_readString(
                 parcel.as_native(),
                 &mut vec as *mut _ as *mut c_void,
@@ -751,11 +772,11 @@
 
 impl Serialize for Status {
     fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+        // Safety: `Parcel` always contains a valid pointer to an `AParcel`
+        // and `Status` always contains a valid pointer to an `AStatus`, so
+        // both parameters are valid and safe. This call does not take
+        // ownership of either of its parameters.
         unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an `AParcel`
-            // and `Status` always contains a valid pointer to an `AStatus`, so
-            // both parameters are valid and safe. This call does not take
-            // ownership of either of its parameters.
             status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native()))
         }
     }
@@ -772,21 +793,18 @@
 
     fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
         let mut status_ptr = ptr::null_mut();
-        let ret_status = unsafe {
-            // Safety: `Parcel` always contains a valid pointer to an
-            // `AParcel`. We pass a mutable out pointer which will be
-            // assigned a valid `AStatus` pointer if the function returns
-            // status OK. This function passes ownership of the status
-            // pointer to the caller, if it was assigned.
-            sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr)
-        };
+        let ret_status =
+        // Safety: `Parcel` always contains a valid pointer to an
+        // `AParcel`. We pass a mutable out pointer which will be
+        // assigned a valid `AStatus` pointer if the function returns
+        // status OK. This function passes ownership of the status
+        // pointer to the caller, if it was assigned.
+            unsafe { sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) };
         status_result(ret_status)?;
-        Ok(unsafe {
-            // Safety: At this point, the return status of the read call was ok,
-            // so we know that `status_ptr` is a valid, owned pointer to an
-            // `AStatus`, from which we can safely construct a `Status` object.
-            Status::from_ptr(status_ptr)
-        })
+        // Safety: At this point, the return status of the read call was ok,
+        // so we know that `status_ptr` is a valid, owned pointer to an
+        // `AStatus`, from which we can safely construct a `Status` object.
+        Ok(unsafe { Status::from_ptr(status_ptr) })
     }
 }
 
@@ -880,7 +898,6 @@
 /// `Serialize`, `SerializeArray` and `SerializeOption` for
 /// structured parcelables. The target type must implement the
 /// `Parcelable` trait.
-/// ```
 #[macro_export]
 macro_rules! impl_serialize_for_parcelable {
     ($parcelable:ident) => {
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index eb82fb7..f906113 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -133,8 +133,8 @@
                 }
             }
             ParcelableHolderData::Parcel(ref mut parcel) => {
+                // Safety: 0 should always be a valid position.
                 unsafe {
-                    // Safety: 0 should always be a valid position.
                     parcel.set_data_position(0)?;
                 }
 
@@ -214,15 +214,15 @@
                 parcelable.write_to_parcel(parcel)?;
 
                 let end = parcel.get_data_position();
+                // Safety: we got the position from `get_data_position`.
                 unsafe {
-                    // Safety: we got the position from `get_data_position`.
                     parcel.set_data_position(length_start)?;
                 }
 
                 assert!(end >= data_start);
                 parcel.write(&(end - data_start))?;
+                // Safety: we got the position from `get_data_position`.
                 unsafe {
-                    // Safety: we got the position from `get_data_position`.
                     parcel.set_data_position(end)?;
                 }
 
@@ -260,11 +260,11 @@
         new_parcel.append_from(parcel, data_start, data_size)?;
         *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
 
+        // Safety: `append_from` checks if `data_size` overflows
+        // `parcel` and returns `BAD_VALUE` if that happens. We also
+        // explicitly check for negative and zero `data_size` above,
+        // so `data_end` is guaranteed to be greater than `data_start`.
         unsafe {
-            // Safety: `append_from` checks if `data_size` overflows
-            // `parcel` and returns `BAD_VALUE` if that happens. We also
-            // explicitly check for negative and zero `data_size` above,
-            // so `data_end` is guaranteed to be greater than `data_start`.
             parcel.set_data_position(data_end)?;
         }
 
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 036f6b4..dad3379 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -49,14 +49,12 @@
     }
 }
 
-/// # Safety
-///
-/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
 unsafe impl Send for SpIBinder {}
 
-/// # Safety
-///
-/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
 unsafe impl Sync for SpIBinder {}
 
 impl SpIBinder {
@@ -97,11 +95,9 @@
     /// Return true if this binder object is hosted in a different process than
     /// the current one.
     pub fn is_remote(&self) -> bool {
-        unsafe {
-            // Safety: `SpIBinder` guarantees that it always contains a valid
-            // `AIBinder` pointer.
-            sys::AIBinder_isRemote(self.as_native())
-        }
+        // Safety: `SpIBinder` guarantees that it always contains a valid
+        // `AIBinder` pointer.
+        unsafe { sys::AIBinder_isRemote(self.as_native()) }
     }
 
     /// Try to convert this Binder object into a trait object for the given
@@ -116,12 +112,12 @@
     /// Return the interface class of this binder object, if associated with
     /// one.
     pub fn get_class(&mut self) -> Option<InterfaceClass> {
+        // Safety: `SpIBinder` guarantees that it always contains a valid
+        // `AIBinder` pointer. `AIBinder_getClass` returns either a null
+        // pointer or a valid pointer to an `AIBinder_Class`. After mapping
+        // null to None, we can safely construct an `InterfaceClass` if the
+        // pointer was non-null.
         unsafe {
-            // Safety: `SpIBinder` guarantees that it always contains a valid
-            // `AIBinder` pointer. `AIBinder_getClass` returns either a null
-            // pointer or a valid pointer to an `AIBinder_Class`. After mapping
-            // null to None, we can safely construct an `InterfaceClass` if the
-            // pointer was non-null.
             let class = sys::AIBinder_getClass(self.as_native_mut());
             class.as_ref().map(|p| InterfaceClass::from_ptr(p))
         }
@@ -152,7 +148,8 @@
     ///
     /// See `SpIBinder::from_raw`.
     pub unsafe fn new_spibinder(ptr: *mut sys::AIBinder) -> Option<SpIBinder> {
-        SpIBinder::from_raw(ptr)
+        // Safety: The caller makes the same guarantees as this requires.
+        unsafe { SpIBinder::from_raw(ptr) }
     }
 }
 
@@ -171,30 +168,24 @@
 
 impl AssociateClass for SpIBinder {
     fn associate_class(&mut self, class: InterfaceClass) -> bool {
-        unsafe {
-            // Safety: `SpIBinder` guarantees that it always contains a valid
-            // `AIBinder` pointer. An `InterfaceClass` can always be converted
-            // into a valid `AIBinder_Class` pointer, so these parameters are
-            // always safe.
-            sys::AIBinder_associateClass(self.as_native_mut(), class.into())
-        }
+        // Safety: `SpIBinder` guarantees that it always contains a valid
+        // `AIBinder` pointer. An `InterfaceClass` can always be converted
+        // into a valid `AIBinder_Class` pointer, so these parameters are
+        // always safe.
+        unsafe { sys::AIBinder_associateClass(self.as_native_mut(), class.into()) }
     }
 }
 
 impl Ord for SpIBinder {
     fn cmp(&self, other: &Self) -> Ordering {
-        let less_than = unsafe {
-            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
-            // this pointer is always safe to pass to `AIBinder_lt` (null is
-            // also safe to pass to this function, but we should never do that).
-            sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr())
-        };
-        let greater_than = unsafe {
-            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
-            // this pointer is always safe to pass to `AIBinder_lt` (null is
-            // also safe to pass to this function, but we should never do that).
-            sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr())
-        };
+        // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this
+        // pointer is always safe to pass to `AIBinder_lt` (null is also safe to
+        // pass to this function, but we should never do that).
+        let less_than = unsafe { sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) };
+        // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this
+        // pointer is always safe to pass to `AIBinder_lt` (null is also safe to
+        // pass to this function, but we should never do that).
+        let greater_than = unsafe { sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) };
         if !less_than && !greater_than {
             Ordering::Equal
         } else if less_than {
@@ -221,10 +212,10 @@
 
 impl Clone for SpIBinder {
     fn clone(&self) -> Self {
+        // Safety: Cloning a strong reference must increment the reference
+        // count. We are guaranteed by the `SpIBinder` constructor
+        // invariants that `self.0` is always a valid `AIBinder` pointer.
         unsafe {
-            // Safety: Cloning a strong reference must increment the reference
-            // count. We are guaranteed by the `SpIBinder` constructor
-            // invariants that `self.0` is always a valid `AIBinder` pointer.
             sys::AIBinder_incStrong(self.0.as_ptr());
         }
         Self(self.0)
@@ -235,9 +226,9 @@
     // We hold a strong reference to the IBinder in SpIBinder and need to give up
     // this reference on drop.
     fn drop(&mut self) {
+        // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
+        // know this pointer is safe to pass to `AIBinder_decStrong` here.
         unsafe {
-            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
-            // know this pointer is safe to pass to `AIBinder_decStrong` here.
             sys::AIBinder_decStrong(self.as_native_mut());
         }
     }
@@ -246,26 +237,24 @@
 impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
     fn prepare_transact(&self) -> Result<Parcel> {
         let mut input = ptr::null_mut();
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. It is safe to cast from an
+        // immutable pointer to a mutable pointer here, because
+        // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
+        // methods but the parameter is unfortunately not marked as const.
+        //
+        // After the call, input will be either a valid, owned `AParcel`
+        // pointer, or null.
         let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. It is safe to cast from an
-            // immutable pointer to a mutable pointer here, because
-            // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
-            // methods but the parameter is unfortunately not marked as const.
-            //
-            // After the call, input will be either a valid, owned `AParcel`
-            // pointer, or null.
             sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
         };
 
         status_result(status)?;
 
-        unsafe {
-            // Safety: At this point, `input` is either a valid, owned `AParcel`
-            // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
-            // taking ownership of the parcel.
-            Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
-        }
+        // Safety: At this point, `input` is either a valid, owned `AParcel`
+        // pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
+        // taking ownership of the parcel.
+        unsafe { Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) }
     }
 
     fn submit_transact(
@@ -275,23 +264,23 @@
         flags: TransactionFlags,
     ) -> Result<Parcel> {
         let mut reply = ptr::null_mut();
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. Although `IBinder::transact` is
+        // not a const method, it is still safe to cast our immutable
+        // pointer to mutable for the call. First, `IBinder::transact` is
+        // thread-safe, so concurrency is not an issue. The only way that
+        // `transact` can affect any visible, mutable state in the current
+        // process is by calling `onTransact` for a local service. However,
+        // in order for transactions to be thread-safe, this method must
+        // dynamically lock its data before modifying it. We enforce this
+        // property in Rust by requiring `Sync` for remotable objects and
+        // only providing `on_transact` with an immutable reference to
+        // `self`.
+        //
+        // This call takes ownership of the `data` parcel pointer, and
+        // passes ownership of the `reply` out parameter to its caller. It
+        // does not affect ownership of the `binder` parameter.
         let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. Although `IBinder::transact` is
-            // not a const method, it is still safe to cast our immutable
-            // pointer to mutable for the call. First, `IBinder::transact` is
-            // thread-safe, so concurrency is not an issue. The only way that
-            // `transact` can affect any visible, mutable state in the current
-            // process is by calling `onTransact` for a local service. However,
-            // in order for transactions to be thread-safe, this method must
-            // dynamically lock its data before modifying it. We enforce this
-            // property in Rust by requiring `Sync` for remotable objects and
-            // only providing `on_transact` with an immutable reference to
-            // `self`.
-            //
-            // This call takes ownership of the `data` parcel pointer, and
-            // passes ownership of the `reply` out parameter to its caller. It
-            // does not affect ownership of the `binder` parameter.
             sys::AIBinder_transact(
                 self.as_native() as *mut sys::AIBinder,
                 code,
@@ -302,45 +291,45 @@
         };
         status_result(status)?;
 
-        unsafe {
-            // Safety: `reply` is either a valid `AParcel` pointer or null
-            // after the call to `AIBinder_transact` above, so we can
-            // construct a `Parcel` out of it. `AIBinder_transact` passes
-            // ownership of the `reply` parcel to Rust, so we need to
-            // construct an owned variant.
-            Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
-        }
+        // Safety: `reply` is either a valid `AParcel` pointer or null
+        // after the call to `AIBinder_transact` above, so we can
+        // construct a `Parcel` out of it. `AIBinder_transact` passes
+        // ownership of the `reply` parcel to Rust, so we need to
+        // construct an owned variant.
+        unsafe { Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) }
     }
 
     fn is_binder_alive(&self) -> bool {
-        unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`.
-            //
-            // This call does not affect ownership of its pointer parameter.
-            sys::AIBinder_isAlive(self.as_native())
-        }
+        // Safety: `SpIBinder` guarantees that `self` always contains a valid
+        // pointer to an `AIBinder`.
+        //
+        // This call does not affect ownership of its pointer parameter.
+        unsafe { sys::AIBinder_isAlive(self.as_native()) }
     }
 
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool) {
+        // Safety: `SpIBinder` guarantees that `self` always contains a valid
+        // pointer to an `AIBinder`.
+        //
+        // This call does not affect ownership of its pointer parameter.
         unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
     }
 
     fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
         let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
         let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
+        // file descriptor parameter is always be a valid open file. The
+        // `args` pointer parameter is a valid pointer to an array of C
+        // strings that will outlive the call since `args` lives for the
+        // whole function scope.
+        //
+        // This call does not affect ownership of its binder pointer
+        // parameter and does not take ownership of the file or args array
+        // parameters.
         let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
-            // file descriptor parameter is always be a valid open file. The
-            // `args` pointer parameter is a valid pointer to an array of C
-            // strings that will outlive the call since `args` lives for the
-            // whole function scope.
-            //
-            // This call does not affect ownership of its binder pointer
-            // parameter and does not take ownership of the file or args array
-            // parameters.
             sys::AIBinder_dump(
                 self.as_native_mut(),
                 fp.as_raw_fd(),
@@ -353,22 +342,18 @@
 
     fn get_extension(&mut self) -> Result<Option<SpIBinder>> {
         let mut out = ptr::null_mut();
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. After this call, the `out`
-            // parameter will be either null, or a valid pointer to an
-            // `AIBinder`.
-            //
-            // This call passes ownership of the out pointer to its caller
-            // (assuming it is set to a non-null value).
-            sys::AIBinder_getExtension(self.as_native_mut(), &mut out)
-        };
-        let ibinder = unsafe {
-            // Safety: The call above guarantees that `out` is either null or a
-            // valid, owned pointer to an `AIBinder`, both of which are safe to
-            // pass to `SpIBinder::from_raw`.
-            SpIBinder::from_raw(out)
-        };
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. After this call, the `out`
+        // parameter will be either null, or a valid pointer to an
+        // `AIBinder`.
+        //
+        // This call passes ownership of the out pointer to its caller
+        // (assuming it is set to a non-null value).
+        let status = unsafe { sys::AIBinder_getExtension(self.as_native_mut(), &mut out) };
+        // Safety: The call above guarantees that `out` is either null or a
+        // valid, owned pointer to an `AIBinder`, both of which are safe to
+        // pass to `SpIBinder::from_raw`.
+        let ibinder = unsafe { SpIBinder::from_raw(out) };
 
         status_result(status)?;
         Ok(ibinder)
@@ -377,17 +362,17 @@
 
 impl<T: AsNative<sys::AIBinder>> IBinder for T {
     fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. `recipient` can always be
+        // converted into a valid pointer to an
+        // `AIBinder_DeathRecipient`.
+        //
+        // The cookie is also the correct pointer, and by calling new_cookie,
+        // we have created a new ref-count to the cookie, which linkToDeath
+        // takes ownership of. Once the DeathRecipient is unlinked for any
+        // reason (including if this call fails), the onUnlinked callback
+        // will consume that ref-count.
         status_result(unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. `recipient` can always be
-            // converted into a valid pointer to an
-            // `AIBinder_DeathRecipient`.
-            //
-            // The cookie is also the correct pointer, and by calling new_cookie,
-            // we have created a new ref-count to the cookie, which linkToDeath
-            // takes ownership of. Once the DeathRecipient is unlinked for any
-            // reason (including if this call fails), the onUnlinked callback
-            // will consume that ref-count.
             sys::AIBinder_linkToDeath(
                 self.as_native_mut(),
                 recipient.as_native_mut(),
@@ -397,13 +382,13 @@
     }
 
     fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`. `recipient` can always be
+        // converted into a valid pointer to an
+        // `AIBinder_DeathRecipient`. Any value is safe to pass as the
+        // cookie, although we depend on this value being set by
+        // `get_cookie` when the death recipient callback is called.
         status_result(unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`. `recipient` can always be
-            // converted into a valid pointer to an
-            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
-            // cookie, although we depend on this value being set by
-            // `get_cookie` when the death recipient callback is called.
             sys::AIBinder_unlinkToDeath(
                 self.as_native_mut(),
                 recipient.as_native_mut(),
@@ -413,13 +398,11 @@
     }
 
     fn ping_binder(&mut self) -> Result<()> {
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`.
-            //
-            // This call does not affect ownership of its pointer parameter.
-            sys::AIBinder_ping(self.as_native_mut())
-        };
+        // Safety: `SpIBinder` guarantees that `self` always contains a
+        // valid pointer to an `AIBinder`.
+        //
+        // This call does not affect ownership of its pointer parameter.
+        let status = unsafe { sys::AIBinder_ping(self.as_native_mut()) };
         status_result(status)
     }
 }
@@ -472,35 +455,31 @@
     }
 }
 
-/// # Safety
-///
-/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
 unsafe impl Send for WpIBinder {}
 
-/// # Safety
-///
-/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is
+/// thread-safe.
 unsafe impl Sync for WpIBinder {}
 
 impl WpIBinder {
     /// Create a new weak reference from an object that can be converted into a
     /// raw `AIBinder` pointer.
     fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
-        let ptr = unsafe {
-            // Safety: `SpIBinder` guarantees that `binder` always contains a
-            // valid pointer to an `AIBinder`.
-            sys::AIBinder_Weak_new(binder.as_native_mut())
-        };
+        // Safety: `SpIBinder` guarantees that `binder` always contains a valid
+        // pointer to an `AIBinder`.
+        let ptr = unsafe { sys::AIBinder_Weak_new(binder.as_native_mut()) };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_new"))
     }
 
     /// Promote this weak reference to a strong reference to the binder object.
     pub fn promote(&self) -> Option<SpIBinder> {
+        // Safety: `WpIBinder` always contains a valid weak reference, so we can
+        // pass this pointer to `AIBinder_Weak_promote`. Returns either null or
+        // an AIBinder owned by the caller, both of which are valid to pass to
+        // `SpIBinder::from_raw`.
         unsafe {
-            // Safety: `WpIBinder` always contains a valid weak reference, so we
-            // can pass this pointer to `AIBinder_Weak_promote`. Returns either
-            // null or an AIBinder owned by the caller, both of which are valid
-            // to pass to `SpIBinder::from_raw`.
             let ptr = sys::AIBinder_Weak_promote(self.0.as_ptr());
             SpIBinder::from_raw(ptr)
         }
@@ -509,35 +488,27 @@
 
 impl Clone for WpIBinder {
     fn clone(&self) -> Self {
-        let ptr = unsafe {
-            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
-            // so this pointer is always safe to pass to `AIBinder_Weak_clone`
-            // (although null is also a safe value to pass to this API).
-            //
-            // We get ownership of the returned pointer, so can construct a new
-            // WpIBinder object from it.
-            sys::AIBinder_Weak_clone(self.0.as_ptr())
-        };
+        // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+        // this pointer is always safe to pass to `AIBinder_Weak_clone`
+        // (although null is also a safe value to pass to this API).
+        //
+        // We get ownership of the returned pointer, so can construct a new
+        // WpIBinder object from it.
+        let ptr = unsafe { sys::AIBinder_Weak_clone(self.0.as_ptr()) };
         Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_clone"))
     }
 }
 
 impl Ord for WpIBinder {
     fn cmp(&self, other: &Self) -> Ordering {
-        let less_than = unsafe {
-            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
-            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
-            // (null is also safe to pass to this function, but we should never
-            // do that).
-            sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr())
-        };
-        let greater_than = unsafe {
-            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
-            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
-            // (null is also safe to pass to this function, but we should never
-            // do that).
-            sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr())
-        };
+        // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+        // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is
+        // also safe to pass to this function, but we should never do that).
+        let less_than = unsafe { sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) };
+        // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so
+        // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is
+        // also safe to pass to this function, but we should never do that).
+        let greater_than = unsafe { sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) };
         if !less_than && !greater_than {
             Ordering::Equal
         } else if less_than {
@@ -564,9 +535,9 @@
 
 impl Drop for WpIBinder {
     fn drop(&mut self) {
+        // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
+        // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
         unsafe {
-            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
-            // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
             sys::AIBinder_Weak_delete(self.0.as_ptr());
         }
     }
@@ -574,7 +545,7 @@
 
 /// Rust wrapper around DeathRecipient objects.
 ///
-/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// The cookie in this struct represents an `Arc<F>` for the owned callback.
 /// This struct owns a ref-count of it, and so does every binder that we
 /// have been linked with.
 ///
@@ -592,17 +563,13 @@
     cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
 }
 
-/// # Safety
-///
-/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer
-/// to a `Fn` which is `Sync` and `Send` (the cookie field). As
+/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and
+/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As
 /// `AIBinder_DeathRecipient` is threadsafe, this structure is too.
 unsafe impl Send for DeathRecipient {}
 
-/// # Safety
-///
-/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer
-/// to a `Fn` which is `Sync` and `Send` (the cookie field). As
+/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and
+/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As
 /// `AIBinder_DeathRecipient` is threadsafe, this structure is too.
 unsafe impl Sync for DeathRecipient {}
 
@@ -614,19 +581,17 @@
         F: Fn() + Send + Sync + 'static,
     {
         let callback: *const F = Arc::into_raw(Arc::new(callback));
-        let recipient = unsafe {
-            // Safety: The function pointer is a valid death recipient callback.
-            //
-            // This call returns an owned `AIBinder_DeathRecipient` pointer
-            // which must be destroyed via `AIBinder_DeathRecipient_delete` when
-            // no longer needed.
-            sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
-        };
+        // Safety: The function pointer is a valid death recipient callback.
+        //
+        // This call returns an owned `AIBinder_DeathRecipient` pointer which
+        // must be destroyed via `AIBinder_DeathRecipient_delete` when no longer
+        // needed.
+        let recipient = unsafe { sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>)) };
+        // Safety: The function pointer is a valid onUnlinked callback.
+        //
+        // All uses of linkToDeath in this file correctly increment the
+        // ref-count that this onUnlinked callback will decrement.
         unsafe {
-            // Safety: The function pointer is a valid onUnlinked callback.
-            //
-            // All uses of linkToDeath in this file correctly increment the
-            // ref-count that this onUnlinked callback will decrement.
             sys::AIBinder_DeathRecipient_setOnUnlinked(
                 recipient,
                 Some(Self::cookie_decr_refcount::<F>),
@@ -648,7 +613,12 @@
     ///
     /// The caller must handle the returned ref-count correctly.
     unsafe fn new_cookie(&self) -> *mut c_void {
-        (self.vtable.cookie_incr_refcount)(self.cookie);
+        // Safety: `cookie_incr_refcount` points to
+        // `Self::cookie_incr_refcount`, and `self.cookie` is the cookie for an
+        // Arc<F>.
+        unsafe {
+            (self.vtable.cookie_incr_refcount)(self.cookie);
+        }
 
         // Return a raw pointer with ownership of a ref-count
         self.cookie
@@ -667,13 +637,14 @@
     ///
     /// # Safety
     ///
-    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// The `cookie` parameter must be the cookie for an `Arc<F>` and
     /// the caller must hold a ref-count to it.
     unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
     where
         F: Fn() + Send + Sync + 'static,
     {
-        let callback = (cookie as *const F).as_ref().unwrap();
+        // Safety: The caller promises that `cookie` is for an Arc<F>.
+        let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
         callback();
     }
 
@@ -682,34 +653,34 @@
     ///
     /// # Safety
     ///
-    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// The `cookie` parameter must be the cookie for an `Arc<F>` and
     /// the owner must give up a ref-count to it.
     unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
     where
         F: Fn() + Send + Sync + 'static,
     {
-        drop(Arc::from_raw(cookie as *const F));
+        // Safety: The caller promises that `cookie` is for an Arc<F>.
+        drop(unsafe { Arc::from_raw(cookie as *const F) });
     }
 
     /// Callback that increments the ref-count.
     ///
     /// # Safety
     ///
-    /// The `cookie` parameter must be the cookie for an Arc<F> and
+    /// The `cookie` parameter must be the cookie for an `Arc<F>` and
     /// the owner must handle the created ref-count properly.
     unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
     where
         F: Fn() + Send + Sync + 'static,
     {
-        let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+        // Safety: The caller promises that `cookie` is for an Arc<F>.
+        let arc = mem::ManuallyDrop::new(unsafe { Arc::from_raw(cookie as *const F) });
         mem::forget(Arc::clone(&arc));
     }
 }
 
-/// # Safety
-///
-/// A `DeathRecipient` is always constructed with a valid raw pointer to an
-/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this
+/// Safety: A `DeathRecipient` is always constructed with a valid raw pointer to
+/// an `AIBinder_DeathRecipient`, so it is always type-safe to extract this
 /// pointer.
 unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient {
     fn as_native(&self) -> *const sys::AIBinder_DeathRecipient {
@@ -723,18 +694,19 @@
 
 impl Drop for DeathRecipient {
     fn drop(&mut self) {
+        // Safety: `self.recipient` is always a valid, owned
+        // `AIBinder_DeathRecipient` pointer returned by
+        // `AIBinder_DeathRecipient_new` when `self` was created. This delete
+        // method can only be called once when `self` is dropped.
         unsafe {
-            // Safety: `self.recipient` is always a valid, owned
-            // `AIBinder_DeathRecipient` pointer returned by
-            // `AIBinder_DeathRecipient_new` when `self` was created. This
-            // delete method can only be called once when `self` is dropped.
             sys::AIBinder_DeathRecipient_delete(self.recipient);
+        }
 
-            // Safety: We own a ref-count to the cookie, and so does every
-            // linked binder. This call gives up our ref-count. The linked
-            // binders should already have given up their ref-count, or should
-            // do so shortly.
-            (self.vtable.cookie_decr_refcount)(self.cookie)
+        // Safety: We own a ref-count to the cookie, and so does every linked
+        // binder. This call gives up our ref-count. The linked binders should
+        // already have given up their ref-count, or should do so shortly.
+        unsafe {
+            (self.vtable.cookie_decr_refcount)(self.cookie);
         }
     }
 }
@@ -754,11 +726,9 @@
     fn from_binder(binder: SpIBinder) -> Result<Self>;
 }
 
-/// # Safety
-///
-/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow
-/// invocation of `IBinder` methods directly from `Interface` objects. It shares
-/// the same safety as the implementation for `SpIBinder`.
+/// Safety: This is a convenience method that wraps `AsNative` for `SpIBinder`
+/// to allow invocation of `IBinder` methods directly from `Interface` objects.
+/// It shares the same safety as the implementation for `SpIBinder`.
 unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T {
     fn as_native(&self) -> *const sys::AIBinder {
         self.as_binder().as_native()
@@ -773,24 +743,20 @@
 /// exist.
 pub fn get_service(name: &str) -> Option<SpIBinder> {
     let name = CString::new(name).ok()?;
-    unsafe {
-        // Safety: `AServiceManager_getService` returns either a null pointer or
-        // a valid pointer to an owned `AIBinder`. Either of these values is
-        // safe to pass to `SpIBinder::from_raw`.
-        SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr()))
-    }
+    // Safety: `AServiceManager_getService` returns either a null pointer or a
+    // valid pointer to an owned `AIBinder`. Either of these values is safe to
+    // pass to `SpIBinder::from_raw`.
+    unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
 }
 
 /// Retrieve an existing service, or start it if it is configured as a dynamic
 /// service and isn't yet started.
 pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
     let name = CString::new(name).ok()?;
-    unsafe {
-        // Safety: `AServiceManager_waitforService` returns either a null
-        // pointer or a valid pointer to an owned `AIBinder`. Either of these
-        // values is safe to pass to `SpIBinder::from_raw`.
-        SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr()))
-    }
+    // Safety: `AServiceManager_waitforService` returns either a null pointer or
+    // a valid pointer to an owned `AIBinder`. Either of these values is safe to
+    // pass to `SpIBinder::from_raw`.
+    unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
 }
 
 /// Retrieve an existing service for a particular interface, blocking for a few
@@ -809,12 +775,10 @@
 pub fn is_declared(interface: &str) -> Result<bool> {
     let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
 
-    unsafe {
-        // Safety: `interface` is a valid null-terminated C-style string and is
-        // only borrowed for the lifetime of the call. The `interface` local
-        // outlives this call as it lives for the function scope.
-        Ok(sys::AServiceManager_isDeclared(interface.as_ptr()))
-    }
+    // Safety: `interface` is a valid null-terminated C-style string and is only
+    // borrowed for the lifetime of the call. The `interface` local outlives
+    // this call as it lives for the function scope.
+    unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
 }
 
 /// Retrieve all declared instances for a particular interface
@@ -827,11 +791,13 @@
         // CString, and outlives this callback. The null handling here is just
         // to avoid the possibility of unwinding across C code if this crate is
         // ever compiled with panic=unwind.
-        if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() {
+        if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
             // Safety: instance is a valid null-terminated C string with a
             // lifetime at least as long as this function, and we immediately
             // copy it into an owned CString.
-            instances.push(CStr::from_ptr(instance).to_owned());
+            unsafe {
+                instances.push(CStr::from_ptr(instance).to_owned());
+            }
         } else {
             eprintln!("Opaque pointer was null in get_declared_instances callback!");
         }
@@ -839,10 +805,10 @@
 
     let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
     let mut instances: Vec<CString> = vec![];
+    // Safety: `interface` and `instances` are borrowed for the length of this
+    // call and both outlive the call. `interface` is guaranteed to be a valid
+    // null-terminated C-style string.
     unsafe {
-        // Safety: `interface` and `instances` are borrowed for the length of
-        // this call and both outlive the call. `interface` is guaranteed to be
-        // a valid null-terminated C-style string.
         sys::AServiceManager_forEachDeclaredInstance(
             interface.as_ptr(),
             &mut instances as *mut _ as *mut c_void,
@@ -860,10 +826,8 @@
         })
 }
 
-/// # Safety
-///
-/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
-/// `AIBinder`, so we can trivially extract this pointer here.
+/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer
+/// to an `AIBinder`, so we can trivially extract this pointer here.
 unsafe impl AsNative<sys::AIBinder> for SpIBinder {
     fn as_native(&self) -> *const sys::AIBinder {
         self.0.as_ptr()
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 4886c5f..a3a2562 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -35,8 +35,8 @@
     /// not work: the callbacks will be queued but never called as there is no
     /// thread to call them on.
     pub fn start_thread_pool() {
+        // Safety: Safe FFI
         unsafe {
-            // Safety: Safe FFI
             sys::ABinderProcess_startThreadPool();
         }
     }
@@ -48,8 +48,8 @@
     /// called, this is 15. If it is called additional times, the thread pool
     /// size can only be increased.
     pub fn set_thread_pool_max_thread_count(num_threads: u32) {
+        // Safety: Safe FFI
         unsafe {
-            // Safety: Safe FFI
             sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads);
         }
     }
@@ -62,8 +62,8 @@
     /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count)
     /// and [`start_thread_pool`](Self::start_thread_pool).
     pub fn join_thread_pool() {
+        // Safety: Safe FFI
         unsafe {
-            // Safety: Safe FFI
             sys::ABinderProcess_joinThreadPool();
         }
     }
@@ -86,10 +86,8 @@
     /// \return calling uid or the current process's UID if this thread isn't
     /// processing a transaction.
     pub fn get_calling_uid() -> uid_t {
-        unsafe {
-            // Safety: Safe FFI
-            sys::AIBinder_getCallingUid()
-        }
+        // Safety: Safe FFI
+        unsafe { sys::AIBinder_getCallingUid() }
     }
 
     /// This returns the calling PID assuming that this thread is called from a
@@ -111,10 +109,8 @@
     /// If the transaction being processed is a oneway transaction, then this
     /// method will return 0.
     pub fn get_calling_pid() -> pid_t {
-        unsafe {
-            // Safety: Safe FFI
-            sys::AIBinder_getCallingPid()
-        }
+        // Safety: Safe FFI
+        unsafe { sys::AIBinder_getCallingPid() }
     }
 
     /// Determine whether the current thread is currently executing an incoming transaction.
@@ -122,10 +118,8 @@
     /// \return true if the current thread is currently executing an incoming transaction, and false
     /// otherwise.
     pub fn is_handling_transaction() -> bool {
-        unsafe {
-            // Safety: Safe FFI
-            sys::AIBinder_isHandlingTransaction()
-        }
+        // Safety: Safe FFI
+        unsafe { sys::AIBinder_isHandlingTransaction() }
     }
 
     /// This function makes the client's security context available to the
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 45c3a90..b268c5d 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -21,6 +21,8 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 
+#include <private/android_filesystem_config.h>
+
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
@@ -40,7 +42,12 @@
     // Always take so that a perturbation of just the one ConsumeBool byte will always
     // take the same path, but with a different UID. Without this, the fuzzer needs to
     // guess both the change in value and the shift at the same time.
-    int64_t maybeSetUid = provider.ConsumeIntegral<int64_t>();
+    int64_t maybeSetUid = provider.PickValueInArray<int64_t>(
+            {static_cast<int64_t>(AID_ROOT) << 32, static_cast<int64_t>(AID_SYSTEM) << 32,
+             provider.ConsumeIntegralInRange<int64_t>(static_cast<int64_t>(AID_ROOT) << 32,
+                                                      static_cast<int64_t>(AID_USER) << 32),
+             provider.ConsumeIntegral<int64_t>()});
+
     if (provider.ConsumeBool()) {
         // set calling uid
         IPCThreadState::self()->restoreCallingIdentity(maybeSetUid);
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
index 7fbf2d0..46205d7 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -20,6 +20,8 @@
 #include <binder/IPCThreadState.h>
 #include <log/log.h>
 
+#include <private/android_filesystem_config.h>
+
 using android::binder::Status;
 
 namespace android {
@@ -29,6 +31,8 @@
     ON_PLAIN,
     ON_BINDER,
     ON_KNOWN_UID,
+    ON_SYSTEM_AID,
+    ON_ROOT_AID,
 };
 
 // This service is to verify that fuzzService is functioning properly
@@ -48,6 +52,18 @@
                 }
                 break;
             }
+            case CrashType::ON_SYSTEM_AID: {
+                if (IPCThreadState::self()->getCallingUid() == AID_SYSTEM) {
+                    LOG_ALWAYS_FATAL("Expected crash, AID_SYSTEM.");
+                }
+                break;
+            }
+            case CrashType::ON_ROOT_AID: {
+                if (IPCThreadState::self()->getCallingUid() == AID_ROOT) {
+                    LOG_ALWAYS_FATAL("Expected crash, AID_ROOT.");
+                }
+                break;
+            }
             default:
                 break;
         }
@@ -99,6 +115,10 @@
         gCrashType = CrashType::ON_PLAIN;
     } else if (arg == "KNOWN_UID") {
         gCrashType = CrashType::ON_KNOWN_UID;
+    } else if (arg == "AID_SYSTEM") {
+        gCrashType = CrashType::ON_SYSTEM_AID;
+    } else if (arg == "AID_ROOT") {
+        gCrashType = CrashType::ON_ROOT_AID;
     } else if (arg == "BINDER") {
         gCrashType = CrashType::ON_BINDER;
     } else {
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index e568035..25906d8 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -27,7 +27,7 @@
     exit 1
 fi
 
-for CRASH_TYPE in PLAIN KNOWN_UID BINDER; do
+for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do
     echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
 
     ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
diff --git a/libs/bufferstreams/README.md b/libs/bufferstreams/README.md
new file mode 100644
index 0000000..860adef
--- /dev/null
+++ b/libs/bufferstreams/README.md
@@ -0,0 +1,13 @@
+# libbufferstreams: Reactive Streams for Graphics Buffers
+
+This library is currently **experimental** and **under active development**.
+It is not production ready yet.
+
+For more information on reactive streams, please see <https://www.reactive-streams.org/>
+
+## Contributing
+
+This library is natively written in Rust and exposes a C API. If you make changes to the Rust API,
+you **must** update the C API in turn. To do so, with cbindgen installed, run:
+
+```$ ./update_include.sh```
diff --git a/libs/bufferstreams/include/bufferstreams.h b/libs/bufferstreams/include/bufferstreams.h
new file mode 100644
index 0000000..5308de2
--- /dev/null
+++ b/libs/bufferstreams/include/bufferstreams.h
@@ -0,0 +1,13 @@
+/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+/**
+ * This function will print Hello World.
+ */
+bool hello(void);
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
new file mode 100644
index 0000000..95a85b5
--- /dev/null
+++ b/libs/bufferstreams/rust/Android.bp
@@ -0,0 +1,23 @@
+// 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.
+
+rust_library {
+    name: "libbufferstreams",
+    crate_name: "bufferstreams",
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    vendor_available: true,
+    host_supported: true,
+    min_sdk_version: "30",
+}
diff --git a/libs/bufferstreams/rust/Cargo.lock b/libs/bufferstreams/rust/Cargo.lock
new file mode 100644
index 0000000..4482dba
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bufferstreams"
+version = "0.1.0"
diff --git a/libs/bufferstreams/rust/Cargo.toml b/libs/bufferstreams/rust/Cargo.toml
new file mode 100644
index 0000000..d30c55c
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "bufferstreams"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/libs/bufferstreams/rust/cbindgen.toml b/libs/bufferstreams/rust/cbindgen.toml
new file mode 100644
index 0000000..eda837f
--- /dev/null
+++ b/libs/bufferstreams/rust/cbindgen.toml
@@ -0,0 +1,149 @@
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+
+
+language = "C"
+
+
+
+############## Options for Wrapping the Contents of the Header #################
+
+# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
+# trailer = "/* Text to put at the end of the generated file */"
+# include_guard = "my_bindings_h"
+# pragma_once = true
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+include_version = false
+# namespace = "my_namespace"
+namespaces = []
+using_namespaces = []
+sys_includes = []
+includes = []
+no_includes = false
+after_includes = ""
+
+
+
+
+############################ Code Style Options ################################
+
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+documentation = true
+documentation_style = "auto"
+documentation_length = "full"
+line_endings = "LF" # also "CR", "CRLF", "Native"
+
+
+
+
+############################# Codegen Options ##################################
+
+style = "both"
+sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
+usize_is_size_t = true
+
+
+
+[defines]
+# "target_os = freebsd" = "DEFINE_FREEBSD"
+# "feature = serde" = "DEFINE_SERDE"
+
+
+
+[export]
+include = []
+exclude = []
+# prefix = "CAPI_"
+item_types = []
+renaming_overrides_prefixing = false
+
+
+
+[export.rename]
+
+
+
+[export.body]
+
+
+[export.mangle]
+
+
+[fn]
+rename_args = "None"
+# must_use = "MUST_USE_FUNC"
+# no_return = "NO_RETURN"
+# prefix = "START_FUNC"
+# postfix = "END_FUNC"
+args = "auto"
+sort_by = "Name"
+
+
+
+
+[struct]
+rename_fields = "None"
+# must_use = "MUST_USE_STRUCT"
+derive_constructor = false
+derive_eq = false
+derive_neq = false
+derive_lt = false
+derive_lte = false
+derive_gt = false
+derive_gte = false
+
+
+
+
+[enum]
+rename_variants = "None"
+# must_use = "MUST_USE_ENUM"
+add_sentinel = false
+prefix_with_name = false
+derive_helper_methods = false
+derive_const_casts = false
+derive_mut_casts = false
+# cast_assert_name = "ASSERT"
+derive_tagged_enum_destructor = false
+derive_tagged_enum_copy_constructor = false
+enum_class = true
+private_default_tagged_enum_constructor = false
+
+
+
+
+[const]
+allow_static_const = true
+allow_constexpr = false
+sort_by = "Name"
+
+
+
+
+[macro_expansion]
+bitflags = false
+
+
+
+
+
+
+############## Options for How Your Rust library Should Be Parsed ##############
+
+[parse]
+parse_deps = false
+# include = []
+exclude = []
+clean = false
+extra_bindings = []
+
+
+
+[parse.expand]
+crates = []
+all_features = false
+default_features = true
+features = []
\ No newline at end of file
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
new file mode 100644
index 0000000..51f1c73
--- /dev/null
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -0,0 +1,22 @@
+// 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.
+
+//! libbufferstreams: Reactive Streams for Graphics Buffers
+
+/// This function will print Hello World.
+#[no_mangle]
+pub extern "C" fn hello() -> bool {
+    println!("Hello world.");
+    true
+}
diff --git a/libs/bufferstreams/update_include.sh b/libs/bufferstreams/update_include.sh
new file mode 100755
index 0000000..e986e9f
--- /dev/null
+++ b/libs/bufferstreams/update_include.sh
@@ -0,0 +1,2 @@
+cd rust
+cbindgen --config cbindgen.toml --crate bufferstreams --output ../include/bufferstreams.h
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 732ca36..715822b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -512,11 +512,7 @@
     return mShouldUseAngle;
 }
 
-// Set ANGLE information.
-// If path is "system", it means system ANGLE must be used for the process.
-// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
-// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
-void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
+void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseSystemAngle,
                                const std::string& packageName,
                                const std::vector<std::string> eglFeatures) {
     if (mShouldUseAngle) {
@@ -533,13 +529,8 @@
     mAnglePath = std::move(path);
     ALOGV("setting app package name to '%s'", packageName.c_str());
     mPackageName = std::move(packageName);
-    if (mAnglePath == "system") {
-        mShouldUseSystemAngle = true;
-    }
-    if (!mAnglePath.empty()) {
-        mShouldUseAngle = true;
-    }
-    mShouldUseNativeDriver = shouldUseNativeDriver;
+    mShouldUseAngle = true;
+    mShouldUseSystemAngle = shouldUseSystemAngle;
 }
 
 std::string& GraphicsEnv::getPackageName() {
@@ -616,10 +607,6 @@
     return mShouldUseSystemAngle;
 }
 
-bool GraphicsEnv::shouldUseNativeDriver() {
-    return mShouldUseNativeDriver;
-}
-
 /**
  * APIs for debuggable layers
  */
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 6cce3f6..fbf2902 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -108,10 +108,7 @@
     // (libraries must be stored uncompressed and page aligned); such elements
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
-    // If the search patch is "system", then it means the system ANGLE should be used.
-    // If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
-    // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
-    void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
+    void setAngleInfo(const std::string& path, const bool useSystemAngle,
                       const std::string& packageName, const std::vector<std::string> eglFeatures);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
@@ -121,7 +118,6 @@
     // Set the persist.graphics.egl system property value.
     void nativeToggleAngleAsSystemDriver(bool enabled);
     bool shouldUseSystemAngle();
-    bool shouldUseNativeDriver();
 
     /*
      * Apis for debug layer
@@ -179,8 +175,6 @@
     bool mShouldUseAngle = false;
     // Whether loader should load system ANGLE.
     bool mShouldUseSystemAngle = false;
-    // Whether loader should load native GLES driver.
-    bool mShouldUseNativeDriver = false;
     // ANGLE namespace.
     android_namespace_t* mAngleNamespace = nullptr;
 
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 75bae76..073cc08 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -90,6 +90,7 @@
     ],
     defaults: [
         "libgui_fuzzer_defaults",
+        "service_fuzzer_defaults",
     ],
 }
 
@@ -100,6 +101,7 @@
     ],
     defaults: [
         "libgui_fuzzer_defaults",
+        "service_fuzzer_defaults",
     ],
 }
 
diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS
new file mode 100644
index 0000000..156efdb
--- /dev/null
+++ b/libs/gui/tests/OWNERS
@@ -0,0 +1,3 @@
+# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces
+# Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
+# Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
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 0b0309e..d9b7700 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -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/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 073da89..8675f14 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -304,6 +304,12 @@
             return std::string("BGRA_8888");
         case android::PIXEL_FORMAT_R_8:
             return std::string("R_8");
+        case android::PIXEL_FORMAT_R_16_UINT:
+            return std::string("R_16_UINT");
+        case android::PIXEL_FORMAT_RG_1616_UINT:
+            return std::string("RG_1616_UINT");
+        case android::PIXEL_FORMAT_RGBA_10101010:
+            return std::string("RGBA_10101010");
         default:
             return StringPrintf("Unknown %#08x", format);
     }
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 654e5b7..8d0eb59 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -169,11 +169,6 @@
         }
     }
 
-    // Return true if native GLES drivers should be used and ANGLE is already loaded.
-    if (android::GraphicsEnv::getInstance().shouldUseNativeDriver() && cnx->angleLoaded) {
-        return true;
-    }
-
     // Return true if updated driver namespace is set.
     ns = android::GraphicsEnv::getInstance().getDriverNamespace();
     if (ns) {
@@ -245,28 +240,16 @@
     if (!hnd) {
         // Secondly, try to load from driver apk.
         hnd = attempt_to_load_updated_driver(cnx);
-
-        // If updated driver apk is set but fail to load, abort here.
-        LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
-                            "couldn't find an OpenGL ES implementation from %s",
-                            android::GraphicsEnv::getInstance().getDriverPath().c_str());
     }
 
-    // Attempt to load native GLES drivers specified by ro.hardware.egl if native is selected.
-    // If native is selected but fail to load, abort.
-    if (!hnd && android::GraphicsEnv::getInstance().shouldUseNativeDriver()) {
-        auto driverSuffix = base::GetProperty(RO_DRIVER_SUFFIX_PROPERTY, "");
-        LOG_ALWAYS_FATAL_IF(driverSuffix.empty(),
-                            "Native GLES driver is selected but not specified in %s",
-                            RO_DRIVER_SUFFIX_PROPERTY);
-        hnd = attempt_to_load_system_driver(cnx, driverSuffix.c_str(), true);
-        LOG_ALWAYS_FATAL_IF(!hnd, "Native GLES driver is selected but failed to load. %s=%s",
-                            RO_DRIVER_SUFFIX_PROPERTY, driverSuffix.c_str());
-    }
-
-    // Finally, try to load default driver.
     bool failToLoadFromDriverSuffixProperty = false;
     if (!hnd) {
+        // If updated driver apk is set but fail to load, abort here.
+        if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
+            LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
+                             android::GraphicsEnv::getInstance().getDriverPath().c_str());
+        }
+        // Finally, try to load system driver.
         // Start by searching for the library name appended by the system
         // properties of the GLES userspace driver in both locations.
         // i.e.:
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ee03d94..f749b0e 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -243,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/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/OWNERS b/services/inputflinger/OWNERS
index c88bfe9..21d208f 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
 include platform/frameworks/base:/INPUT_OWNERS
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 abb0610..7611d68 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);
@@ -3302,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()
@@ -3333,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 &&
@@ -3349,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;
             }
@@ -4093,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;
         }
     }
@@ -6569,7 +6560,7 @@
     }
 }
 
-void InputDispatcher::dump(std::string& dump) {
+void InputDispatcher::dump(std::string& dump) const {
     std::scoped_lock _l(mLock);
 
     dump += "Input Dispatcher State:\n";
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9cd1666..cb87067 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -87,7 +87,7 @@
                              std::chrono::nanoseconds staleEventTimeout);
     ~InputDispatcher() override;
 
-    void dump(std::string& dump) override;
+    void dump(std::string& dump) const override;
     void monitor() override;
     bool waitForIdle() const override;
     status_t start() override;
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 f13885a..4ddfee0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -39,7 +39,7 @@
     /* Dumps the state of the input dispatcher.
      *
      * This method may be called on any thread (usually by the input manager). */
-    virtual void dump(std::string& dump) = 0;
+    virtual void dump(std::string& dump) const = 0;
 
     /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
     virtual void monitor() = 0;
diff --git a/include/input/EventBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
similarity index 60%
rename from include/input/EventBuilders.h
rename to services/inputflinger/include/NotifyArgsBuilders.h
index 09438e9..e4363a4 100644
--- a/include/input/EventBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -19,145 +19,15 @@
 #include <NotifyArgs.h>
 #include <android/input.h>
 #include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
 #include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <utils/Timers.h> // for nsecs_t, systemTime
+
 #include <vector>
 
 namespace android {
 
-// An arbitrary device id.
-static constexpr uint32_t DEFAULT_DEVICE_ID = 1;
-
-// The default policy flags to use for event injection by tests.
-static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
-
-class PointerBuilder {
-public:
-    PointerBuilder(int32_t id, ToolType toolType) {
-        mProperties.clear();
-        mProperties.id = id;
-        mProperties.toolType = toolType;
-        mCoords.clear();
-    }
-
-    PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
-
-    PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
-
-    PointerBuilder& axis(int32_t axis, float value) {
-        mCoords.setAxisValue(axis, value);
-        return *this;
-    }
-
-    PointerProperties buildProperties() const { return mProperties; }
-
-    PointerCoords buildCoords() const { return mCoords; }
-
-private:
-    PointerProperties mProperties;
-    PointerCoords mCoords;
-};
-
-class MotionEventBuilder {
-public:
-    MotionEventBuilder(int32_t action, int32_t source) {
-        mAction = action;
-        mSource = source;
-        mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        mDownTime = mEventTime;
-    }
-
-    MotionEventBuilder& deviceId(int32_t deviceId) {
-        mDeviceId = deviceId;
-        return *this;
-    }
-
-    MotionEventBuilder& downTime(nsecs_t downTime) {
-        mDownTime = downTime;
-        return *this;
-    }
-
-    MotionEventBuilder& eventTime(nsecs_t eventTime) {
-        mEventTime = eventTime;
-        return *this;
-    }
-
-    MotionEventBuilder& displayId(int32_t displayId) {
-        mDisplayId = displayId;
-        return *this;
-    }
-
-    MotionEventBuilder& actionButton(int32_t actionButton) {
-        mActionButton = actionButton;
-        return *this;
-    }
-
-    MotionEventBuilder& buttonState(int32_t buttonState) {
-        mButtonState = buttonState;
-        return *this;
-    }
-
-    MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
-        mRawXCursorPosition = rawXCursorPosition;
-        return *this;
-    }
-
-    MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
-        mRawYCursorPosition = rawYCursorPosition;
-        return *this;
-    }
-
-    MotionEventBuilder& pointer(PointerBuilder pointer) {
-        mPointers.push_back(pointer);
-        return *this;
-    }
-
-    MotionEventBuilder& addFlag(uint32_t flags) {
-        mFlags |= flags;
-        return *this;
-    }
-
-    MotionEvent build() {
-        std::vector<PointerProperties> pointerProperties;
-        std::vector<PointerCoords> pointerCoords;
-        for (const PointerBuilder& pointer : mPointers) {
-            pointerProperties.push_back(pointer.buildProperties());
-            pointerCoords.push_back(pointer.buildCoords());
-        }
-
-        // Set mouse cursor position for the most common cases to avoid boilerplate.
-        if (mSource == AINPUT_SOURCE_MOUSE &&
-            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
-            mRawXCursorPosition = pointerCoords[0].getX();
-            mRawYCursorPosition = pointerCoords[0].getY();
-        }
-
-        MotionEvent event;
-        static const ui::Transform kIdentityTransform;
-        event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
-                         mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
-                         MotionClassification::NONE, kIdentityTransform,
-                         /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
-                         mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
-                         mPointers.size(), pointerProperties.data(), pointerCoords.data());
-        return event;
-    }
-
-private:
-    int32_t mAction;
-    int32_t mDeviceId{DEFAULT_DEVICE_ID};
-    int32_t mSource;
-    nsecs_t mDownTime;
-    nsecs_t mEventTime;
-    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
-    int32_t mActionButton{0};
-    int32_t mButtonState{0};
-    int32_t mFlags{0};
-    float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
-    float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
-
-    std::vector<PointerBuilder> mPointers;
-};
-
 class MotionArgsBuilder {
 public:
     MotionArgsBuilder(int32_t action, int32_t source) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 035d96d..2aaddf5 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -66,38 +66,23 @@
     return enabled;
 }
 
-std::list<NotifyArgs> InputDevice::updateEnableState(nsecs_t when,
-                                                     const InputReaderConfiguration& readerConfig,
-                                                     bool forceEnable) {
-    bool enable = true;
-    if (!forceEnable) {
-        // If the device was explicitly disabled by the user, it would be present in the
-        // "disabledDevices" list. This device should be disabled.
-        enable = readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end();
-
-        // If a device is associated with a specific display but there is no
-        // associated DisplayViewport, don't enable the device.
-        if (enable && (mAssociatedDisplayPort || mAssociatedDisplayUniqueId) &&
-            !mAssociatedViewport) {
-            const std::string desc = mAssociatedDisplayPort
-                    ? "port " + std::to_string(*mAssociatedDisplayPort)
-                    : "uniqueId " + *mAssociatedDisplayUniqueId;
-            ALOGW("Cannot enable input device %s because it is associated "
-                  "with %s, but the corresponding viewport is not found",
-                  getName().c_str(), desc.c_str());
-            enable = false;
-        }
+std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    std::list<NotifyArgs> out;
+    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+              "but the corresponding viewport is not found",
+              getName().c_str(), *mAssociatedDisplayPort);
+        enabled = false;
     }
 
-    std::list<NotifyArgs> out;
-    if (isEnabled() == enable) {
+    if (isEnabled() == enabled) {
         return out;
     }
 
     // When resetting some devices, the driver needs to be queried to ensure that a proper reset is
     // performed. The querying must happen when the device is enabled, so we reset after enabling
     // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
-    if (enable) {
+    if (enabled) {
         for_each_subdevice([](auto& context) { context.enableDevice(); });
         out += reset(when);
     } else {
@@ -173,23 +158,18 @@
     mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
 }
 
-[[nodiscard]] std::list<NotifyArgs> InputDevice::addEventHubDevice(
-        nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig) {
+void InputDevice::addEventHubDevice(int32_t eventHubId,
+                                    const InputReaderConfiguration& readerConfig) {
     if (mDevices.find(eventHubId) != mDevices.end()) {
-        return {};
+        return;
     }
+    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
+    std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
 
-    // Add an empty device configure and keep it enabled to allow mapper population
-    // with correct configuration/context
-    addEmptyEventHubDevice(eventHubId);
-    std::list<NotifyArgs> out = configure(when, readerConfig, {}, /*forceEnable=*/true);
-
-    DevicePair& devicePair = mDevices[eventHubId];
-    devicePair.second = createMappers(*devicePair.first, readerConfig);
-
+    // insert the context into the devices set
+    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
     // Must change generation to flag this device as changed
     bumpGeneration();
-    return out;
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
@@ -202,7 +182,7 @@
 
 std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
                                              const InputReaderConfiguration& readerConfig,
-                                             ConfigurationChanges changes, bool forceEnable) {
+                                             ConfigurationChanges changes) {
     std::list<NotifyArgs> out;
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
@@ -272,6 +252,15 @@
             }
         }
 
+        if (changes.test(Change::ENABLED_STATE)) {
+            // Do not execute this code on the first configure, because 'setEnabled' would call
+            // InputMapper::reset, and you can't reset a mapper before it has been configured.
+            // The mappers are configured for the first time at the bottom of this function.
+            auto it = readerConfig.disabledDevices.find(mId);
+            bool enabled = it == readerConfig.disabledDevices.end();
+            out += setEnabled(enabled, when);
+        }
+
         if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
             // In most situations, no port or name will be specified.
             mAssociatedDisplayPort = std::nullopt;
@@ -295,8 +284,12 @@
                 }
             }
 
-            // If it is associated with a specific display, then find the corresponding viewport
-            // which will be used to enable/disable the device.
+            // If the device was explicitly disabled by the user, it would be present in the
+            // "disabledDevices" list. If it is associated with a specific display, and it was not
+            // explicitly disabled, then enable/disable the device based on whether we can find the
+            // corresponding viewport.
+            bool enabled =
+                    (readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end());
             if (mAssociatedDisplayPort) {
                 mAssociatedViewport =
                         readerConfig.getDisplayViewportByPort(*mAssociatedDisplayPort);
@@ -304,6 +297,7 @@
                     ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
                           "but the corresponding viewport is not found.",
                           getName().c_str(), *mAssociatedDisplayPort);
+                    enabled = false;
                 }
             } else if (mAssociatedDisplayUniqueId != std::nullopt) {
                 mAssociatedViewport =
@@ -312,21 +306,30 @@
                     ALOGW("Input device %s should be associated with display %s but the "
                           "corresponding viewport cannot be found",
                           getName().c_str(), mAssociatedDisplayUniqueId->c_str());
+                    enabled = false;
                 }
             }
-        }
 
-        if (!changes.any() || changes.test(Change::ENABLED_STATE) ||
-            changes.test(Change::DISPLAY_INFO)) {
-            // Whether a device is enabled can depend on the display association,
-            // so update the enabled state when there is a change in display info.
-            out += updateEnableState(when, readerConfig, forceEnable);
+            if (changes.any()) {
+                // For first-time configuration, only allow device to be disabled after mappers have
+                // finished configuring. This is because we need to read some of the properties from
+                // the device's open fd.
+                out += setEnabled(enabled, when);
+            }
         }
 
         for_each_mapper([this, when, &readerConfig, changes, &out](InputMapper& mapper) {
             out += mapper.reconfigure(when, readerConfig, changes);
             mSources |= mapper.getSources();
         });
+
+        // If a device is just plugged but it might be disabled, we need to update some info like
+        // axis range of touch from each InputMapper first, then disable it.
+        if (!changes.any()) {
+            out += setEnabled(readerConfig.disabledDevices.find(mId) ==
+                                      readerConfig.disabledDevices.end(),
+                              when);
+        }
     }
     return out;
 }
@@ -519,9 +522,9 @@
         classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
         mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
-        mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH)) {
-        mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
     }
 
     // Joystick-like devices.
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 7d0f28b..ea95f78 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -234,7 +234,7 @@
     }
 
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
-    std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier);
+    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
 
     notifyAll(device->configure(when, mConfig, /*changes=*/{}));
     notifyAll(device->reset(when));
@@ -319,7 +319,7 @@
 }
 
 std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
-        nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) {
+        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
     auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
         const InputDeviceIdentifier identifier2 =
                 devicePair.second->getDeviceInfo().getIdentifier();
@@ -334,7 +334,7 @@
         device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                                identifier);
     }
-    notifyAll(device->addEventHubDevice(when, eventHubId, mConfig));
+    device->addEventHubDevice(eventHubId, mConfig);
     return device;
 }
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index d92f4a2..aae3fe7 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -77,16 +77,15 @@
     inline bool isIgnored() { return !getMapperCount() && !mController; }
 
     bool isEnabled();
+    [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump, const std::string& eventHubDevStr);
     void addEmptyEventHubDevice(int32_t eventHubId);
-    [[nodiscard]] std::list<NotifyArgs> addEventHubDevice(
-            nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig);
+    void addEventHubDevice(int32_t eventHubId, const InputReaderConfiguration& readerConfig);
     void removeEventHubDevice(int32_t eventHubId);
     [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
                                                   const InputReaderConfiguration& readerConfig,
-                                                  ConfigurationChanges changes,
-                                                  bool forceEnable = false);
+                                                  ConfigurationChanges changes);
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
     [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
@@ -207,9 +206,6 @@
     std::vector<std::unique_ptr<InputMapper>> createMappers(
             InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig);
 
-    [[nodiscard]] std::list<NotifyArgs> updateEnableState(
-            nsecs_t when, const InputReaderConfiguration& readerConfig, bool forceEnable = false);
-
     PropertyMap mConfiguration;
 
     // Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 391a889..9112913 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -121,7 +121,7 @@
 
 protected:
     // These members are protected so they can be instrumented by test cases.
-    virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
                                                             const InputDeviceIdentifier& identifier)
             REQUIRES(mLock);
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..f300ee1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,6 +27,8 @@
     friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
                                                 const InputReaderConfiguration& readerConfig,
                                                 Args... args);
+    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+                                   const InputReaderConfiguration& readerConfig);
 
     ~MultiTouchInputMapper() override;
 
@@ -39,8 +41,6 @@
     bool hasStylus() const override;
 
 private:
-    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
-                                   const InputReaderConfiguration& readerConfig);
     // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
     // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
     // It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..dac53cf 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,6 +27,8 @@
     friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
                                                 const InputReaderConfiguration& readerConfig,
                                                 Args... args);
+    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+                                    const InputReaderConfiguration& readerConfig);
 
     ~SingleTouchInputMapper() override;
 
@@ -40,8 +42,6 @@
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
-                                    const InputReaderConfiguration& readerConfig);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f48ff4c..b565454 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2009,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;
         }
     }
@@ -2756,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;
         }
     }
@@ -3543,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;
@@ -3582,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,
@@ -3723,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);
 
@@ -3740,8 +3737,8 @@
 
     // 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;
@@ -3795,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/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index ca4dd1e..986dabb 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -58,13 +58,14 @@
 };
 
 const std::vector<CurveSegment> segments = {
-        {10.922, 3.19, 0},
-        {31.750, 4.79, -17.526},
-        {98.044, 7.28, -96.52},
-        {std::numeric_limits<double>::infinity(), 15.04, -857.758},
+        {32.002, 3.19, 0},
+        {52.83, 4.79, -51.254},
+        {119.124, 7.28, -182.737},
+        {std::numeric_limits<double>::infinity(), 15.04, -1107.556},
 };
 
-const std::vector<double> sensitivityFactors = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18};
+const std::vector<double> sensitivityFactors = {1,  2,  4,  6,  7,  8,  9, 10,
+                                                11, 12, 13, 14, 16, 18, 20};
 
 std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
                                                           size_t propertySize) {
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 1088821..e826341 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -239,6 +239,11 @@
         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
                                      newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
                                      &coords, xCursorPosition, yCursorPosition));
+        // Send a HOVER_MOVE to tell the application that the mouse is hovering again.
+        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                     /*actionButton=*/0, newButtonState, /*pointerCount=*/1,
+                                     mFingerProps.data(), &coords, xCursorPosition,
+                                     yCursorPosition));
     }
     mButtonState = newButtonState;
     return out;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index a3994f0..482a266 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -169,7 +169,7 @@
                            /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
                            /* is_tap= */ false);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
-    ASSERT_EQ(2u, args.size());
+    ASSERT_EQ(3u, args.size());
 
     ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
@@ -181,6 +181,10 @@
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
                       WithCoords(POINTER_X, POINTER_Y),
                       WithToolType(ToolType::FINGER)));
+    args.pop_front();
+    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+                      WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER)));
 }
 
 TEST_F(GestureConverterTest, DragWithButton) {
@@ -225,7 +229,7 @@
                       /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
                       /* is_tap= */ false);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
-    ASSERT_EQ(2u, args.size());
+    ASSERT_EQ(3u, args.size());
 
     ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
@@ -237,6 +241,10 @@
                 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
                       WithCoords(POINTER_X - 5, POINTER_Y + 10),
                       WithToolType(ToolType::FINGER)));
+    args.pop_front();
+    ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
+                      WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER)));
 }
 
 TEST_F(GestureConverterTest, Scroll) {
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 484e7d6..2ff64c8 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -16,9 +16,10 @@
 
 #include "../InputDeviceMetricsCollector.h"
 
+#include <NotifyArgsBuilders.h>
 #include <gtest/gtest.h>
 #include <gui/constants.h>
-#include <input/EventBuilders.h>
+#include <input/InputEventBuilders.h>
 #include <linux/input.h>
 
 #include <array>
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 6a6312e..d367cd7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,6 +17,7 @@
 #include "../dispatcher/InputDispatcher.h"
 #include "../BlockingQueue.h"
 
+#include <NotifyArgsBuilders.h>
 #include <android-base/properties.h>
 #include <android-base/silent_death_test.h>
 #include <android-base/stringprintf.h>
@@ -25,7 +26,6 @@
 #include <fcntl.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <input/EventBuilders.h>
 #include <input/Input.h>
 #include <linux/input.h>
 #include <sys/epoll.h>
@@ -6606,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.
@@ -6676,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.
@@ -6740,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);
@@ -6791,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);
@@ -6815,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);
@@ -7031,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
@@ -7136,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));
@@ -7145,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.
@@ -7231,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();
@@ -7253,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();
 }
 
@@ -7361,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
@@ -7449,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);
@@ -7462,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);
 
@@ -7473,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,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 4fd30c0..477beaf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2553,7 +2553,7 @@
 
 // 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 configure API is called
+// 2. Device is disabled when setEnabled API is called
 TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
                                         AINPUT_SOURCE_TOUCHSCREEN);
@@ -2660,8 +2660,7 @@
     mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
 
     InputDevice device(mReader->getContext(), /*id=*/1, /*generation=*/2, /*identifier=*/{});
-    auto _ = device.addEventHubDevice(ARBITRARY_TIME, TEST_EVENTHUB_ID,
-                                      mFakePolicy->getReaderConfiguration());
+    device.addEventHubDevice(TEST_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
     device.removeEventHubDevice(TEST_EVENTHUB_ID);
     std::string dumpStr, eventHubDevStr;
     device.dump(dumpStr, eventHubDevStr);
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
index 110ca5f..1f8cd12 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.cpp
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -38,13 +38,13 @@
 }
 
 std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
-        nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+        int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
     if (!mNextDevices.empty()) {
         std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
         mNextDevices.pop();
         return device;
     }
-    return InputReader::createDeviceLocked(when, eventHubId, identifier);
+    return InputReader::createDeviceLocked(eventHubId, identifier);
 }
 
 } // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index e6bf3f9..7f8d556 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -44,7 +44,7 @@
 
 protected:
     virtual std::shared_ptr<InputDevice> createDeviceLocked(
-            nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier);
+            int32_t eventHubId, const InputDeviceIdentifier& identifier);
 
     class FakeInputReaderContext : public ContextImpl {
     public:
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 92cd462..02abf9f 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -139,7 +139,8 @@
                             VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
                             VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
                             VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
-                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
 
     // Liftoff
     args.clear();
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 993f6a8..e93b592 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -16,6 +16,7 @@
 
 #include <CursorInputMapper.h>
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
 #include <MapperHelpers.h>
 
 namespace android {
@@ -39,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.
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index fd14e02..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:
@@ -42,8 +40,8 @@
 
         // 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 d11e8ae..54977df 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
 #include <KeyboardInputMapper.h>
 #include <MapperHelpers.h>
 
@@ -45,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.
@@ -65,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>()));
                 },
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 4a2c98c..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>
@@ -92,6 +95,7 @@
     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:
@@ -111,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(); }
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 494b0ef..569767f 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
 #include <MapperHelpers.h>
 #include <MultiTouchInputMapper.h>
 
@@ -62,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.
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index 381e7b5..80eebd5 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <FuzzContainer.h>
+#include <InputReaderBase.h>
 #include <MapperHelpers.h>
 #include <SwitchInputMapper.h>
 
@@ -25,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) {
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/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index b00d1a7..e939d51 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -141,7 +141,7 @@
                 printf("ALOOPER_POLL_TIMEOUT\n");
                 break;
             case ALOOPER_POLL_ERROR:
-                printf("ALOOPER_POLL_TIMEOUT\n");
+                printf("ALOOPER_POLL_ERROR\n");
                 break;
             default:
                 printf("ugh? poll returned %d\n", ret);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 89c80bc..326645e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -168,6 +168,7 @@
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "HdrLayerInfoReporter.cpp",
+        "HdrSdrRatioOverlay.cpp",
         "WindowInfosListenerInvoker.cpp",
         "Layer.cpp",
         "LayerFE.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index c512a1e..9713e79 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -67,7 +67,7 @@
     out.append("\n   ");
 
     out.append("\n   ");
-    dumpVal(out, "treate170mAsSrgb", treat170mAsSrgb);
+    dumpVal(out, "treat170mAsSrgb", treat170mAsSrgb);
 
     out.append("\n");
     for (const auto& borderRenderInfo : borderInfoList) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index a6521bb..7547be9 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -180,18 +180,19 @@
             .targetLuminanceNits = outputState.displayBrightnessNits,
     };
 
-    LayerFE::ClientCompositionTargetSettings targetSettings{
-            .clip = Region(viewport),
-            .needsFiltering = false,
-            .isSecure = outputState.isSecure,
-            .supportsProtectedContent = false,
-            .viewport = viewport,
-            .dataspace = outputDataspace,
-            .realContentIsVisible = true,
-            .clearContent = false,
-            .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
-            .whitePointNits = outputState.displayBrightnessNits,
-    };
+    LayerFE::ClientCompositionTargetSettings
+            targetSettings{.clip = Region(viewport),
+                           .needsFiltering = false,
+                           .isSecure = outputState.isSecure,
+                           .supportsProtectedContent = false,
+                           .viewport = viewport,
+                           .dataspace = outputDataspace,
+                           .realContentIsVisible = true,
+                           .clearContent = false,
+                           .blurSetting =
+                                   LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+                           .whitePointNits = outputState.displayBrightnessNits,
+                           .treat170mAsSrgb = outputState.treat170mAsSrgb};
 
     std::vector<renderengine::LayerSettings> layerSettings;
     renderengine::LayerSettings highlight;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f6ca9e2..32bd890 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,11 +37,11 @@
 #include <configstore/Utils.h>
 #include <log/log.h>
 #include <system/window.h>
-#include <ui/GraphicTypes.h>
 
 #include "Display/DisplaySnapshot.h"
 #include "DisplayDevice.h"
 #include "FrontEnd/DisplayInfo.h"
+#include "HdrSdrRatioOverlay.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
 #include "SurfaceFlinger.h"
@@ -261,6 +261,9 @@
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->setLayerStack(filter.layerStack);
     }
+    if (mHdrSdrRatioOverlay) {
+        mHdrSdrRatioOverlay->setLayerStack(filter.layerStack);
+    }
 }
 
 void DisplayDevice::setFlags(uint32_t flags) {
@@ -274,10 +277,14 @@
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->setViewport(size);
     }
+    if (mHdrSdrRatioOverlay) {
+        mHdrSdrRatioOverlay->setViewport(size);
+    }
 }
 
 void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
                                   Rect orientedDisplaySpaceRect) {
+    mIsOrientationChanged = mOrientation != orientation;
     mOrientation = orientation;
 
     // We need to take care of display rotation for globalTransform for case if the panel is not
@@ -411,6 +418,26 @@
                            capabilities.getDesiredMinLuminance());
 }
 
+void DisplayDevice::enableHdrSdrRatioOverlay(bool enable) {
+    if (!enable) {
+        mHdrSdrRatioOverlay.reset();
+        return;
+    }
+
+    mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>();
+    mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
+    mHdrSdrRatioOverlay->setViewport(getSize());
+    updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+}
+
+void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
+    ATRACE_CALL();
+    mHdrSdrRatio = currentHdrSdrRatio;
+    if (mHdrSdrRatioOverlay) {
+        mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio);
+    }
+}
+
 void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
                                              bool showRenderRate, bool showInMiddle) {
     if (!enable) {
@@ -463,10 +490,23 @@
     return false;
 }
 
-void DisplayDevice::animateRefreshRateOverlay() {
+void DisplayDevice::animateOverlay() {
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->animate();
     }
+    if (mHdrSdrRatioOverlay) {
+        // hdr sdr ratio is designed to be on the top right of the screen,
+        // therefore, we need to re-calculate the display's width and height
+        if (mIsOrientationChanged) {
+            auto width = getWidth();
+            auto height = getHeight();
+            if (mOrientation == ui::ROTATION_90 || mOrientation == ui::ROTATION_270) {
+                std::swap(width, height);
+            }
+            mHdrSdrRatioOverlay->setViewport({width, height});
+        }
+        mHdrSdrRatioOverlay->animate();
+    }
 }
 
 auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index dc5f8a8..e92125a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -55,6 +55,7 @@
 
 class Fence;
 class HWComposer;
+class HdrSdrRatioOverlay;
 class IGraphicBufferProducer;
 class Layer;
 class RefreshRateOverlay;
@@ -235,13 +236,19 @@
         return mRefreshRateSelector;
     }
 
+    void animateOverlay();
+
     // Enables an overlay to be displayed with the current refresh rate
     void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
                                   bool showInMiddle) REQUIRES(kMainThreadContext);
     void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
-    void animateRefreshRateOverlay();
+
+    // Enables an overlay to be display with the hdr/sdr ratio
+    void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
+    void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
+    bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
 
     nsecs_t getVsyncPeriodFromHWC() const;
 
@@ -271,6 +278,7 @@
 
     const ui::Rotation mPhysicalOrientation;
     ui::Rotation mOrientation = ui::ROTATION_0;
+    bool mIsOrientationChanged = false;
 
     // Allow nullopt as initial power mode.
     using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
@@ -297,6 +305,9 @@
 
     std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
     std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+    std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay;
+    // This parameter is only used for hdr/sdr ratio overlay
+    float mHdrSdrRatio = 1.0f;
 
     mutable std::mutex mActiveModeLock;
     ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index c0eb36d..311820c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -244,14 +244,13 @@
     addReader(translate<Display>(kSingleReaderKey));
 
     // If unable to read interface version, then become backwards compatible.
-    int32_t version = 1;
-    const auto status = mAidlComposerClient->getInterfaceVersion(&version);
+    const auto status = mAidlComposerClient->getInterfaceVersion(&mComposerInterfaceVersion);
     if (!status.isOk()) {
         ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
               status.getDescription().c_str());
     }
-    mSupportsBufferSlotsToClear = version > 1;
-    if (!mSupportsBufferSlotsToClear) {
+
+    if (mComposerInterfaceVersion <= 1) {
         if (sysprop::clear_slots_with_set_layer_buffer(false)) {
             mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
                                                        GraphicBuffer::USAGE_HW_COMPOSER |
@@ -281,6 +280,10 @@
     }
 }
 
+bool AidlComposer::getDisplayConfigurationsSupported() const {
+    return mComposerInterfaceVersion >= 3;
+}
+
 std::vector<Capability> AidlComposer::getCapabilities() {
     std::vector<Capability> capabilities;
     const auto status = mAidlComposer->getCapabilities(&capabilities);
@@ -489,6 +492,18 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getDisplayConfigurations(Display display,
+                                             std::vector<DisplayConfiguration>* outConfigs) {
+    const auto status =
+            mAidlComposerClient->getDisplayConfigurations(translate<int64_t>(display), outConfigs);
+    if (!status.isOk()) {
+        ALOGE("getDisplayConfigurations failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+
+    return Error::NONE;
+}
+
 Error AidlComposer::getDisplayName(Display display, std::string* outName) {
     const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
     if (!status.isOk()) {
@@ -848,7 +863,7 @@
     Error error = Error::NONE;
     mMutex.lock_shared();
     if (auto writer = getWriter(display)) {
-        if (mSupportsBufferSlotsToClear) {
+        if (mComposerInterfaceVersion > 1) {
             writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
                                                      translate<int64_t>(layer), slotsToClear);
             // Backwards compatible way of clearing buffer slots is to set the layer buffer with a
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 8d21b49..e31ff81 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -66,6 +66,7 @@
     ~AidlComposer() override;
 
     bool isSupported(OptionalFeature) const;
+    bool getDisplayConfigurationsSupported() const;
 
     std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
             override;
@@ -95,6 +96,7 @@
     Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
                               int32_t* outValue) override;
     Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayConfigurations(Display, std::vector<DisplayConfiguration>*);
     Error getDisplayName(Display display, std::string* outName) override;
 
     Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
@@ -285,8 +287,8 @@
     // threading annotations.
     ftl::SharedMutex mMutex;
 
-    // Whether or not explicitly clearing buffer slots is supported.
-    bool mSupportsBufferSlotsToClear;
+    int32_t mComposerInterfaceVersion = 1;
+
     // Buffer slots for layers are cleared by setting the slot buffer to this buffer.
     sp<GraphicBuffer> mClearSlotBuffer;
 
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index cf67795..cc60fd0 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -39,6 +39,7 @@
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
@@ -85,6 +86,7 @@
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
 using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
 using aidl::android::hardware::graphics::common::Hdr;
 
 class Composer {
@@ -103,6 +105,7 @@
     };
 
     virtual bool isSupported(OptionalFeature) const = 0;
+    virtual bool getDisplayConfigurationsSupported() const = 0;
 
     virtual std::vector<aidl::android::hardware::graphics::composer3::Capability>
     getCapabilities() = 0;
@@ -130,6 +133,9 @@
     virtual Error getDisplayAttribute(Display display, Config config,
                                       IComposerClient::Attribute attribute, int32_t* outValue) = 0;
     virtual Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs) = 0;
+
+    virtual Error getDisplayConfigurations(Display, std::vector<DisplayConfiguration>*) = 0;
+
     virtual Error getDisplayName(Display display, std::string* outName) = 0;
 
     virtual Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 61a9a08..1810925 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -80,20 +80,20 @@
             return *this;
         }
 
-        Builder& setDpiX(int32_t dpiX) {
-            if (dpiX == -1) {
+        Builder& setDpiX(float dpiX) {
+            if (dpiX == -1.f) {
                 mDisplayMode->mDpi.x = getDefaultDensity();
             } else {
-                mDisplayMode->mDpi.x = dpiX / 1000.f;
+                mDisplayMode->mDpi.x = dpiX;
             }
             return *this;
         }
 
-        Builder& setDpiY(int32_t dpiY) {
-            if (dpiY == -1) {
+        Builder& setDpiY(float dpiY) {
+            if (dpiY == -1.f) {
                 mDisplayMode->mDpi.y = getDefaultDensity();
             } else {
-                mDisplayMode->mDpi.y = dpiY / 1000.f;
+                mDisplayMode->mDpi.y = dpiY;
             }
             return *this;
         }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f350eba..aefa7c3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -265,6 +265,46 @@
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
+
+    if (mComposer->getDisplayConfigurationsSupported()) {
+        return getModesFromDisplayConfigurations(hwcDisplayId);
+    }
+
+    return getModesFromLegacyDisplayConfigs(hwcDisplayId);
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations(
+        uint64_t hwcDisplayId) const {
+    std::vector<hal::DisplayConfiguration> configs;
+    auto error =
+            static_cast<hal::Error>(mComposer->getDisplayConfigurations(hwcDisplayId, &configs));
+    RETURN_IF_HWC_ERROR_FOR("getDisplayConfigurations", error, *toPhysicalDisplayId(hwcDisplayId),
+                            {});
+
+    std::vector<HWCDisplayMode> modes;
+    modes.reserve(configs.size());
+    for (auto config : configs) {
+        auto hwcMode = HWCDisplayMode{
+                .hwcId = static_cast<hal::HWConfigId>(config.configId),
+                .width = config.width,
+                .height = config.height,
+                .vsyncPeriod = config.vsyncPeriod,
+                .configGroup = config.configGroup,
+        };
+
+        if (config.dpi) {
+            hwcMode.dpiX = config.dpi->x;
+            hwcMode.dpiY = config.dpi->y;
+        }
+
+        modes.push_back(hwcMode);
+    }
+
+    return modes;
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayConfigs(
+        uint64_t hwcDisplayId) const {
     std::vector<hal::HWConfigId> configIds;
     auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds));
     RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId), {});
@@ -272,17 +312,25 @@
     std::vector<HWCDisplayMode> modes;
     modes.reserve(configIds.size());
     for (auto configId : configIds) {
-        modes.push_back(HWCDisplayMode{
+        auto hwcMode = HWCDisplayMode{
                 .hwcId = configId,
                 .width = getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH),
                 .height = getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT),
                 .vsyncPeriod = getAttribute(hwcDisplayId, configId, hal::Attribute::VSYNC_PERIOD),
-                .dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X),
-                .dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y),
                 .configGroup = getAttribute(hwcDisplayId, configId, hal::Attribute::CONFIG_GROUP),
-        });
-    }
+        };
 
+        const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
+        const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
+        if (dpiX != -1) {
+            hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f;
+        }
+        if (dpiY != -1) {
+            hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f;
+        }
+
+        modes.push_back(hwcMode);
+    }
     return modes;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3702c62..8247d97 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -99,8 +99,8 @@
         int32_t width = -1;
         int32_t height = -1;
         nsecs_t vsyncPeriod = -1;
-        int32_t dpiX = -1;
-        int32_t dpiY = -1;
+        float dpiX = -1.f;
+        float dpiY = -1.f;
         int32_t configGroup = -1;
 
         friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
@@ -501,6 +501,9 @@
     std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
     bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
 
+    std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId) const;
+    std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const;
+
     int32_t getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
                          hal::Attribute attribute) const;
 
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index bf3089f..e95ae89 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -23,6 +23,7 @@
 #include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
 
 #define ERROR_HAS_CHANGES 5
 
@@ -34,6 +35,7 @@
 namespace V2_2 = android::hardware::graphics::composer::V2_2;
 namespace V2_3 = android::hardware::graphics::composer::V2_3;
 namespace V2_4 = android::hardware::graphics::composer::V2_4;
+namespace V3_0 = ::aidl::android::hardware::graphics::composer3;
 
 using types::V1_0::ColorTransform;
 using types::V1_0::Transform;
@@ -70,6 +72,7 @@
 using Vsync = IComposerClient::Vsync;
 using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
 using Hdr = aidl::android::hardware::graphics::common::Hdr;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
 
 } // namespace hardware::graphics::composer::hal
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 9b41da5..0655abc 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -269,6 +269,11 @@
     }
 }
 
+bool HidlComposer::getDisplayConfigurationsSupported() const {
+    // getDisplayConfigurations is not supported on the HIDL composer.
+    return false;
+};
+
 std::vector<Capability> HidlComposer::getCapabilities() {
     std::vector<Capability> capabilities;
     mComposer->getCapabilities([&](const auto& tmpCapabilities) {
@@ -477,6 +482,11 @@
     return error;
 }
 
+Error HidlComposer::getDisplayConfigurations(Display, std::vector<DisplayConfiguration>*) {
+    LOG_ALWAYS_FATAL("getDisplayConfigurations should not have been called on this, as "
+                     "it's a HWC3 interface version 3 feature");
+}
+
 Error HidlComposer::getDisplayName(Display display, std::string* outName) {
     Error error = kDefaultError;
     mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 0521acf..ac96d9a 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -167,6 +167,7 @@
     ~HidlComposer() override;
 
     bool isSupported(OptionalFeature) const;
+    bool getDisplayConfigurationsSupported() const;
 
     std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
             override;
@@ -196,6 +197,7 @@
     Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
                               int32_t* outValue) override;
     Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayConfigurations(Display, std::vector<DisplayConfiguration>*);
     Error getDisplayName(Display display, std::string* outName) override;
 
     Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
new file mode 100644
index 0000000..2c0f518
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -0,0 +1,178 @@
+/**
+ * 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.
+ */
+// #define LOG_NDEBUG 0
+#include <algorithm>
+
+#include "HdrSdrRatioOverlay.h"
+
+#include <SkSurface.h>
+
+#undef LOG_TAG
+#define LOG_TAG "HdrSdrRatioOverlay"
+
+namespace android {
+
+void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
+    if (!isfinite(number) || number >= 10.f) return;
+    // We assume that the number range is [1.f, 10.f)
+    // and the decimal places are 2.
+    int value = static_cast<int>(number * 100);
+    SegmentDrawer::drawDigit(value / 100, left, color, canvas);
+
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
+    left += kDigitWidth + kDigitSpace;
+
+    SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawDigit(value % 10, left, color, canvas);
+}
+
+sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
+                                           ui::Transform::RotationFlags rotation) {
+    SkMatrix canvasTransform = SkMatrix();
+    const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
+        switch (rotation) {
+            case ui::Transform::ROT_90:
+                canvasTransform.setTranslate(kBufferHeight, 0);
+                canvasTransform.preRotate(90.f);
+                return {kBufferHeight, kBufferWidth};
+            case ui::Transform::ROT_270:
+                canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
+                return {kBufferHeight, kBufferWidth};
+            default:
+                return {kBufferWidth, kBufferHeight};
+        }
+    }();
+
+    const auto kUsageFlags = static_cast<uint64_t>(
+            GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
+    sp<GraphicBuffer> buffer =
+            sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+                                    static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
+                                    1u, kUsageFlags, "HdrSdrRatioOverlay");
+
+    const status_t bufferStatus = buffer->initCheck();
+    LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
+                        bufferStatus);
+
+    sk_sp<SkSurface> surface =
+            SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->setMatrix(canvasTransform);
+
+    drawNumber(currentHdrSdrRatio, 0, color, *canvas);
+
+    void* pixels = nullptr;
+    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+
+    const SkImageInfo& imageInfo = surface->imageInfo();
+    const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
+
+    canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
+    buffer->unlock();
+    return buffer;
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+      : mSurfaceControl(
+                SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
+    if (!mSurfaceControl) {
+        ALOGE("%s: Failed to create buffer state layer", __func__);
+        return;
+    }
+    SurfaceComposerClient::Transaction()
+            .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+            .setTrustedOverlay(mSurfaceControl->get(), true)
+            .apply();
+}
+
+void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
+    mCurrentHdrSdrRatio = currentHdrSdrRatio;
+    animate();
+}
+
+void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
+    SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+}
+
+void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
+    constexpr int32_t kMaxWidth = 1000;
+    const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
+    const auto height = 2 * width;
+    Rect frame((5 * width) >> 4, height >> 5);
+    // set the ratio frame to the top right of the screen
+    frame.offsetBy(viewport.width - frame.width(), height >> 4);
+
+    SurfaceComposerClient::Transaction()
+            .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+                       0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+            .setPosition(mSurfaceControl->get(), frame.left, frame.top)
+            .apply();
+}
+
+auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
+    static const sp<GraphicBuffer> kNoBuffer;
+    if (!mSurfaceControl) return kNoBuffer;
+
+    const auto transformHint =
+            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
+
+    // Tell SurfaceFlinger about the pre-rotation on the buffer.
+    const auto transform = [&] {
+        switch (transformHint) {
+            case ui::Transform::ROT_90:
+                return ui::Transform::ROT_270;
+            case ui::Transform::ROT_270:
+                return ui::Transform::ROT_90;
+            default:
+                return ui::Transform::ROT_0;
+        }
+    }();
+
+    SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+
+    constexpr SkColor kMinRatioColor = SK_ColorBLUE;
+    constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
+    constexpr float kAlpha = 0.8f;
+
+    // 9.f is picked here as ratio range, given that we assume that
+    // hdr/sdr ratio is [1.f, 10.f)
+    const float scale = currentHdrSdrRatio / 9.f;
+
+    SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
+    const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
+
+    colorBase.fR = colorBase.fR + minRatioColor.fR;
+    colorBase.fG = colorBase.fG + minRatioColor.fG;
+    colorBase.fB = colorBase.fB + minRatioColor.fB;
+    colorBase.fA = kAlpha;
+
+    const SkColor color = colorBase.toSkColor();
+
+    auto buffer = draw(currentHdrSdrRatio, color, transformHint);
+    return buffer;
+}
+
+void HdrSdrRatioOverlay::animate() {
+    if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
+
+    SurfaceComposerClient::Transaction()
+            .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
+            .apply();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
new file mode 100644
index 0000000..8a2586e
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -0,0 +1,45 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "Utils/OverlayUtils.h"
+
+#include <ui/Size.h>
+#include <utils/StrongPointer.h>
+
+class SkCanvas;
+
+namespace android {
+class HdrSdrRatioOverlay {
+public:
+    HdrSdrRatioOverlay();
+    void setLayerStack(ui::LayerStack);
+    void setViewport(ui::Size);
+    void animate();
+    void changeHdrSdrRatio(float currentRatio);
+
+private:
+    float mCurrentHdrSdrRatio = 1.f;
+
+    static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags);
+    static void drawNumber(float number, int left, SkColor, SkCanvas&);
+
+    const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
+
+    const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
+};
+} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 607bec2..577211f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,104 +16,20 @@
 
 #include <algorithm>
 
-#include "BackgroundExecutor.h"
 #include "Client.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#pragma clang diagnostic pop
-#include <SkBlendMode.h>
-#include <SkRect.h>
 #include <SkSurface.h>
-#include <gui/SurfaceControl.h>
 
 #undef LOG_TAG
 #define LOG_TAG "RefreshRateOverlay"
 
 namespace android {
-namespace {
 
-constexpr int kDigitWidth = 64;
-constexpr int kDigitHeight = 100;
-constexpr int kDigitSpace = 16;
-
-constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
-constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
-constexpr int kBufferHeight = kDigitHeight;
-
-} // namespace
-
-SurfaceControlHolder::~SurfaceControlHolder() {
-    // Hand the sp<SurfaceControl> to the helper thread to release the last
-    // reference. This makes sure that the SurfaceControl is destructed without
-    // SurfaceFlinger::mStateLock held.
-    BackgroundExecutor::getInstance().sendCallbacks(
-            {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
-                                                         SkCanvas& canvas) {
-    const SkRect rect = [&]() {
-        switch (segment) {
-            case Segment::Upper:
-                return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
-            case Segment::UpperLeft:
-                return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2);
-            case Segment::UpperRight:
-                return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
-                                        kDigitHeight / 2);
-            case Segment::Middle:
-                return SkRect::MakeLTRB(left, kDigitHeight / 2 - kDigitSpace / 2,
-                                        left + kDigitWidth, kDigitHeight / 2 + kDigitSpace / 2);
-            case Segment::LowerLeft:
-                return SkRect::MakeLTRB(left, kDigitHeight / 2, left + kDigitSpace, kDigitHeight);
-            case Segment::LowerRight:
-                return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2,
-                                        left + kDigitWidth, kDigitHeight);
-            case Segment::Bottom:
-                return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
-                                        kDigitHeight);
-        }
-    }();
-
-    SkPaint paint;
-    paint.setColor(color);
-    paint.setBlendMode(SkBlendMode::kSrc);
-    canvas.drawRect(rect, paint);
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor color,
-                                                       SkCanvas& canvas) {
-    if (digit < 0 || digit > 9) return;
-
-    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
-        digit == 8 || digit == 9)
-        drawSegment(Segment::Upper, left, color, canvas);
-    if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
-        drawSegment(Segment::UpperLeft, left, color, canvas);
-    if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
-        digit == 8 || digit == 9)
-        drawSegment(Segment::UpperRight, left, color, canvas);
-    if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
-        digit == 9)
-        drawSegment(Segment::Middle, left, color, canvas);
-    if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
-        drawSegment(Segment::LowerLeft, left, color, canvas);
-    if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
-        digit == 7 || digit == 8 || digit == 9)
-        drawSegment(Segment::LowerRight, left, color, canvas);
-    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
-        digit == 9)
-        drawSegment(Segment::Bottom, left, color, canvas);
-}
-
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
-                                                  ui::Transform::RotationFlags rotation,
-                                                  ftl::Flags<Features> features) -> Buffers {
+auto RefreshRateOverlay::draw(int displayFps, int renderFps, SkColor color,
+                              ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
+        -> Buffers {
     const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
 
     Buffers buffers;
@@ -159,22 +75,27 @@
         if (features.test(Features::Spinner)) {
             switch (i) {
                 case 0:
-                    drawSegment(Segment::Upper, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::Upper, left, color, *canvas);
                     break;
                 case 1:
-                    drawSegment(Segment::UpperRight, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperRight, left, color,
+                                               *canvas);
                     break;
                 case 2:
-                    drawSegment(Segment::LowerRight, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerRight, left, color,
+                                               *canvas);
                     break;
                 case 3:
-                    drawSegment(Segment::Bottom, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::Bottom, left, color,
+                                               *canvas);
                     break;
                 case 4:
-                    drawSegment(Segment::LowerLeft, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerLeft, left, color,
+                                               *canvas);
                     break;
                 case 5:
-                    drawSegment(Segment::UpperLeft, left, color, *canvas);
+                    SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperLeft, left, color,
+                                               *canvas);
                     break;
             }
         }
@@ -200,34 +121,27 @@
     return buffers;
 }
 
-void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
-                                                        SkCanvas& canvas) {
+void RefreshRateOverlay::drawNumber(int number, int left, SkColor color, SkCanvas& canvas) {
     if (number < 0 || number >= 1000) return;
 
     if (number >= 100) {
-        drawDigit(number / 100, left, color, canvas);
+        SegmentDrawer::drawDigit(number / 100, left, color, canvas);
     }
     left += kDigitWidth + kDigitSpace;
 
     if (number >= 10) {
-        drawDigit((number / 10) % 10, left, color, canvas);
+        SegmentDrawer::drawDigit((number / 10) % 10, left, color, canvas);
     }
     left += kDigitWidth + kDigitSpace;
 
-    drawDigit(number % 10, left, color, canvas);
-}
-
-std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
-    sp<SurfaceControl> surfaceControl =
-            SurfaceComposerClient::getDefault()
-                    ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
-                                    PIXEL_FORMAT_RGBA_8888,
-                                    ISurfaceComposerClient::eFXSurfaceBufferState);
-    return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+    SegmentDrawer::drawDigit(number % 10, left, color, canvas);
 }
 
 RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
-      : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
+      : mFpsRange(fpsRange),
+        mFeatures(features),
+        mSurfaceControl(
+                SurfaceControlHolder::createSurfaceControlHolder(String8("RefreshRateOverlay"))) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
@@ -296,8 +210,7 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
-                                                mFeatures);
+        auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures);
         it = mBufferCache
                      .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
                      .first;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 0b89b8e..65c61cb 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
-#include <SkColor.h>
+#include "Utils/OverlayUtils.h"
+
 #include <vector>
 
 #include <ftl/flags.h>
 #include <ftl/small_map.h>
-#include <gui/SurfaceComposerClient.h>
 #include <ui/LayerStack.h>
 #include <ui/Size.h>
 #include <ui/Transform.h>
@@ -34,22 +34,8 @@
 namespace android {
 
 class GraphicBuffer;
-class SurfaceControl;
 class SurfaceFlinger;
 
-// Helper class to delete the SurfaceControl on a helper thread as
-// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
-class SurfaceControlHolder {
-public:
-    explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
-    ~SurfaceControlHolder();
-
-    const sp<SurfaceControl>& get() const { return mSurfaceControl; }
-
-private:
-    sp<SurfaceControl> mSurfaceControl;
-};
-
 class RefreshRateOverlay {
 public:
     enum class Features {
@@ -70,18 +56,9 @@
 private:
     using Buffers = std::vector<sp<GraphicBuffer>>;
 
-    class SevenSegmentDrawer {
-    public:
-        static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
-                            ftl::Flags<Features>);
-
-    private:
-        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
-
-        static void drawSegment(Segment, int left, SkColor, SkCanvas&);
-        static void drawDigit(int digit, int left, SkColor, SkCanvas&);
-        static void drawNumber(int number, int left, SkColor, SkCanvas&);
-    };
+    static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+                        ftl::Flags<Features>);
+    static void drawNumber(int number, int left, SkColor, SkCanvas&);
 
     const Buffers& getOrCreateBuffers(Fps, Fps);
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 281b0ae..c70ed2c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -427,11 +427,6 @@
     mCondition.notify_all();
 }
 
-size_t EventThread::getEventThreadConnectionCount() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mDisplayEventConnections.size();
-}
-
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 684745b..7023445 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -131,9 +131,6 @@
     virtual VsyncEventData getLatestVsyncEventData(
             const sp<EventThreadConnection>& connection) const = 0;
 
-    // Retrieves the number of event connections tracked by this EventThread.
-    virtual size_t getEventThreadConnectionCount() = 0;
-
     virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
 };
 
@@ -172,8 +169,6 @@
     void setDuration(std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration) override;
 
-    size_t getEventThreadConnectionCount() override;
-
     void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
 
 private:
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..08667bf 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) {
@@ -382,12 +393,6 @@
     thread->onModeChanged(mode);
 }
 
-size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
-    std::lock_guard<std::mutex> lock(mConnectionsLock);
-    RETURN_IF_INVALID_HANDLE(handle, 0);
-    return mConnections[handle].thread->getEventThreadConnectionCount();
-}
-
 void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
     android::EventThread* thread;
     {
@@ -555,6 +560,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 +581,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 +591,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 +848,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..0ffa2d2 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();
 
@@ -274,8 +278,6 @@
     // Notifies the scheduler when the display size has changed. Called from SF's main thread
     void onActiveDisplayAreaChanged(uint32_t displayArea);
 
-    size_t getEventThreadConnectionCount(ConnectionHandle handle);
-
     // Stores the preferred refresh rate that an app should run at.
     // FrameRateOverride.refreshRateHz == 0 means no preference.
     void setPreferredRefreshRateForUid(FrameRateOverride);
@@ -310,9 +312,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 +385,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 +511,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/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 6499d69..e0fb8f9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -146,13 +146,14 @@
     void cancelTimer() REQUIRES(mMutex);
     ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
 
+    std::mutex mutable mMutex;
+
     static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
     std::unique_ptr<TimeKeeper> const mTimeKeeper;
     VsyncSchedule::TrackerPtr mTracker;
     nsecs_t const mTimerSlack;
     nsecs_t const mMinVsyncDistance;
 
-    std::mutex mutable mMutex;
     size_t mCallbackToken GUARDED_BY(mMutex) = 0;
 
     CallbackMap mCallbacks GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 09dac23..0103843 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -24,6 +24,24 @@
 
 namespace android {
 
+namespace {
+
+ui::Size getDisplaySize(ui::Rotation orientation, const Rect& sourceCrop) {
+    if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
+        return {sourceCrop.getHeight(), sourceCrop.getWidth()};
+    }
+    return {sourceCrop.getWidth(), sourceCrop.getHeight()};
+}
+
+Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int reqHeight) {
+    if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
+        return {reqHeight, reqWidth};
+    }
+    return {reqWidth, reqHeight};
+}
+
+} // namespace
+
 std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
     std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
             ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
@@ -37,6 +55,7 @@
     output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
     output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
     output->editState().clientTargetBrightness = args.targetBrightness;
+    output->editState().treat170mAsSrgb = args.treat170mAsSrgb;
 
     output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
             compositionengine::DisplayColorProfileCreationArgsBuilder()
@@ -45,10 +64,10 @@
 
     const Rect& sourceCrop = args.renderArea.getSourceCrop();
     const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
-    const Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(),
-                                        args.renderArea.getReqHeight()};
-    output->setProjection(orientation, sourceCrop, orientedDisplaySpaceRect);
-    output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
+    output->setDisplaySize(getDisplaySize(orientation, sourceCrop));
+    output->setProjection(orientation, sourceCrop,
+                          getOrientedDisplaySpaceRect(orientation, args.renderArea.getReqWidth(),
+                                                      args.renderArea.getReqHeight()));
 
     {
         std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 3c307b0..159c2bf 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -36,6 +36,7 @@
     // Counterintuitively, when targetBrightness > 1.0 then dim the scene.
     float targetBrightness;
     bool regionSampling;
+    bool treat170mAsSrgb;
 };
 
 // ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c46da11..a9a1d80 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1963,6 +1963,11 @@
 
                        FTL_FAKE_GUARD(kMainThreadContext,
                                       display->stageBrightness(brightness.displayBrightness));
+                       float currentHdrSdrRatio =
+                               compositionDisplay->editState().displayBrightnessNits /
+                               compositionDisplay->editState().sdrWhitePointNits;
+                       FTL_FAKE_GUARD(kMainThreadContext,
+                                      display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio));
 
                        if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits !=
                            currentDimmingRatio) {
@@ -2325,6 +2330,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;
 
@@ -2454,10 +2464,10 @@
         mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
     }
 
-    if (mRefreshRateOverlaySpinner) {
+    if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) {
         Mutex::Autolock lock(mStateLock);
         if (const auto display = getDefaultDisplayDeviceLocked()) {
-            display->animateRefreshRateOverlay();
+            display->animateOverlay();
         }
     }
 
@@ -2501,13 +2511,21 @@
     // 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();
     }
 
     updateCursorAsync();
-    updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+    if (!mustComposite) {
+        updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+    }
 
     if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and tracing should only be enabled for debugging.
@@ -2702,6 +2720,8 @@
         addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
     }
 
+    updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+
     if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
     mVisibleRegionsDirty = false;
 
@@ -2991,10 +3011,6 @@
         }
     }
 
-    const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
-    const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
-    mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
-
     if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
         getRenderEngine().cleanupPostRender();
         return;
@@ -3924,6 +3940,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;
 
@@ -6462,9 +6486,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1042 are currently used for backdoors. The code
+    // Numbers from 1000 to 1043 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1042) {
+    if (code >= 1000 && code <= 1043) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -6925,6 +6949,24 @@
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
             }
+            // hdr sdr ratio overlay
+            case 1043: {
+                auto future = mScheduler->schedule(
+                        [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                            n = data.readInt32();
+                            mHdrSdrRatioOverlay = n != 0;
+                            switch (n) {
+                                case 0:
+                                case 1:
+                                    enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay);
+                                    break;
+                                default:
+                                    reply->writeBool(isHdrSdrRatioOverlayEnabled());
+                            }
+                        });
+                future.wait();
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -7648,7 +7690,8 @@
                                         .sdrWhitePointNits = sdrWhitePointNits,
                                         .displayBrightnessNits = displayBrightnessNits,
                                         .targetBrightness = targetBrightness,
-                                        .regionSampling = regionSampling});
+                                        .regionSampling = regionSampling,
+                                        .treat170mAsSrgb = mTreat170mAsSrgb});
 
         const float colorSaturation = grayscale ? 0 : 1;
         compositionengine::CompositionRefreshArgs refreshArgs{
@@ -7938,6 +7981,7 @@
     if (mTransactionTracing) {
         mTransactionTracing->onLayerRemoved(layer->getSequence());
     }
+    mScheduler->onLayerDestroyed(layer);
 }
 
 void SurfaceFlinger::onLayerUpdate() {
@@ -8023,6 +8067,16 @@
     }
 }
 
+void SurfaceFlinger::enableHdrSdrRatioOverlay(bool enable) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+            if (const auto device = getDisplayDeviceLocked(id)) {
+                device->enableHdrSdrRatioOverlay(enable);
+            }
+        }
+    }
+}
+
 int SurfaceFlinger::getGpuContextPriority() {
     return getRenderEngine().getContextPriority();
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d97a747..aeaeb47 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -646,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;
@@ -672,6 +673,8 @@
     bool mRefreshRateOverlayRenderRate = false;
     // Show render rate overlay offseted to the middle of the screen (e.g. for circular displays)
     bool mRefreshRateOverlayShowInMiddle = false;
+    // Show hdr sdr ratio overlay
+    bool mHdrSdrRatioOverlay = false;
 
     void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
             REQUIRES(mStateLock);
@@ -1181,6 +1184,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;
@@ -1288,7 +1292,6 @@
     ui::Dataspace mDefaultCompositionDataspace;
     ui::Dataspace mWideColorGamutCompositionDataspace;
     ui::Dataspace mColorSpaceAgnosticDataspace;
-    float mDimmingRatio = -1.f;
 
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
     std::atomic<int> mNumTrustedPresentationListeners = 0;
@@ -1336,6 +1339,8 @@
 
     void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
+    void enableHdrSdrRatioOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
+
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
 
@@ -1381,6 +1386,10 @@
         return hasDisplay(
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
     }
+    bool isHdrSdrRatioOverlayEnabled() const REQUIRES(mStateLock) {
+        return hasDisplay(
+                [](const auto& display) { return display.isHdrSdrRatioOverlayEnabled(); });
+    }
     std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
             std::optional<ui::LayerStack> layerStack, uint32_t uid,
             std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 630cef1..368cb41 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -106,7 +106,8 @@
         atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
         atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
         atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
-        atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+        // Deprecated
+        atom->set_event_connection_count(0);
         *atom->mutable_frame_duration() =
                 histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
         *atom->mutable_render_engine_timing() =
@@ -356,16 +357,6 @@
     mTimeStats.refreshRateSwitchesLegacy++;
 }
 
-void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    mTimeStats.displayEventConnectionsCountLegacy =
-            std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
-}
-
 static int32_t toMs(nsecs_t nanos) {
     int64_t millis =
             std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
@@ -1072,7 +1063,6 @@
     mTimeStats.compositionStrategyPredictedLegacy = 0;
     mTimeStats.compositionStrategyPredictionSucceededLegacy = 0;
     mTimeStats.refreshRateSwitchesLegacy = 0;
-    mTimeStats.displayEventConnectionsCountLegacy = 0;
     mTimeStats.displayOnTimeLegacy = 0;
     mTimeStats.presentToPresentLegacy.hist.clear();
     mTimeStats.frameDurationLegacy.hist.clear();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5f58657..0c227d4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -57,9 +57,6 @@
     virtual void incrementMissedFrames() = 0;
     // Increments the number of times the display refresh rate changed.
     virtual void incrementRefreshRateSwitches() = 0;
-    // Records the most up-to-date count of display event connections.
-    // The stored count will be the maximum ever recoded.
-    virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
 
     // Records the start and end times for a frame.
     // The start time is the same as the beginning of a SurfaceFlinger
@@ -253,7 +250,6 @@
     void incrementTotalFrames() override;
     void incrementMissedFrames() override;
     void incrementRefreshRateSwitches() override;
-    void recordDisplayEventConnectionCount(int32_t count) override;
 
     void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
     void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 60aa810..9e97f0d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -175,7 +175,6 @@
         int32_t clientCompositionReusedFramesLegacy = 0;
         int32_t refreshRateSwitchesLegacy = 0;
         int32_t compositionStrategyChangesLegacy = 0;
-        int32_t displayEventConnectionsCountLegacy = 0;
         int64_t displayOnTimeLegacy = 0;
         Histogram presentToPresentLegacy;
         Histogram frameDurationLegacy;
diff --git a/services/surfaceflinger/Utils/OverlayUtils.h b/services/surfaceflinger/Utils/OverlayUtils.h
new file mode 100644
index 0000000..0ef0be1
--- /dev/null
+++ b/services/surfaceflinger/Utils/OverlayUtils.h
@@ -0,0 +1,146 @@
+/**
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#pragma once
+
+#include "BackgroundExecutor.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#pragma clang diagnostic pop
+
+#include <gui/SurfaceComposerClient.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+inline constexpr int kDigitWidth = 64;
+inline constexpr int kDigitHeight = 100;
+inline constexpr int kDigitSpace = 16;
+
+// HdrSdrRatioOverlay re-uses this value though it doesn't really need such amount buffer.
+// for output good-looking and code conciseness.
+inline constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+inline constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
+inline constexpr int kBufferHeight = kDigitHeight;
+
+class SurfaceControl;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+    explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+
+    ~SurfaceControlHolder() {
+        // Hand the sp<SurfaceControl> to the helper thread to release the last
+        // reference. This makes sure that the SurfaceControl is destructed without
+        // SurfaceFlinger::mStateLock held.
+        BackgroundExecutor::getInstance().sendCallbacks(
+                {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+    }
+
+    static std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder(const String8& name) {
+        sp<SurfaceControl> surfaceControl =
+                SurfaceComposerClient::getDefault()
+                        ->createSurface(name, kBufferWidth, kBufferHeight, PIXEL_FORMAT_RGBA_8888,
+                                        ISurfaceComposerClient::eFXSurfaceBufferState);
+        return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+    }
+
+    const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+    sp<SurfaceControl> mSurfaceControl;
+};
+
+// Helper class to draw digit and decimal point.
+class SegmentDrawer {
+public:
+    enum class Segment {
+        Upper,
+        UpperLeft,
+        UpperRight,
+        Middle,
+        LowerLeft,
+        LowerRight,
+        Bottom,
+        DecimalPoint
+    };
+    static void drawSegment(Segment segment, int left, SkColor color, SkCanvas& canvas) {
+        const SkRect rect = [&]() {
+            switch (segment) {
+                case Segment::Upper:
+                    return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
+                case Segment::UpperLeft:
+                    return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2.);
+                case Segment::UpperRight:
+                    return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
+                                            kDigitHeight / 2.);
+                case Segment::Middle:
+                    return SkRect::MakeLTRB(left, kDigitHeight / 2. - kDigitSpace / 2.,
+                                            left + kDigitWidth,
+                                            kDigitHeight / 2. + kDigitSpace / 2.);
+                case Segment::LowerLeft:
+                    return SkRect::MakeLTRB(left, kDigitHeight / 2., left + kDigitSpace,
+                                            kDigitHeight);
+                case Segment::LowerRight:
+                    return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2.,
+                                            left + kDigitWidth, kDigitHeight);
+                case Segment::Bottom:
+                    return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
+                                            kDigitHeight);
+                case Segment::DecimalPoint:
+                    return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitSpace,
+                                            kDigitHeight);
+            }
+        }();
+
+        SkPaint paint;
+        paint.setColor(color);
+        paint.setBlendMode(SkBlendMode::kSrc);
+        canvas.drawRect(rect, paint);
+    }
+
+    static void drawDigit(int digit, int left, SkColor color, SkCanvas& canvas) {
+        if (digit < 0 || digit > 9) return;
+
+        if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+            digit == 8 || digit == 9)
+            drawSegment(Segment::Upper, left, color, canvas);
+        if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+            drawSegment(Segment::UpperLeft, left, color, canvas);
+        if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+            digit == 8 || digit == 9)
+            drawSegment(Segment::UpperRight, left, color, canvas);
+        if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+            digit == 9)
+            drawSegment(Segment::Middle, left, color, canvas);
+        if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+            drawSegment(Segment::LowerLeft, left, color, canvas);
+        if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+            digit == 7 || digit == 8 || digit == 9)
+            drawSegment(Segment::LowerRight, left, color, canvas);
+        if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+            digit == 9)
+            drawSegment(Segment::Bottom, left, color, canvas);
+    }
+};
+
+} // namespace android
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/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 62b539a..b5168b0 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -37,8 +37,9 @@
         "DisplayConfigs_test.cpp",
         "DisplayEventReceiver_test.cpp",
         "EffectLayer_test.cpp",
-        "LayerBorder_test.cpp",
+        "HdrSdrRatioOverlay_test.cpp",
         "InvalidHandles_test.cpp",
+        "LayerBorder_test.cpp",
         "LayerCallback_test.cpp",
         "LayerRenderTypeTransaction_test.cpp",
         "LayerState_test.cpp",
diff --git a/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
new file mode 100644
index 0000000..77a8f9c
--- /dev/null
+++ b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
@@ -0,0 +1,89 @@
+/**
+ * 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <chrono>
+
+using ::std::literals::chrono_literals::operator""s;
+
+static constexpr int kHdrSdrRatioOverlayCode = 1043;
+static constexpr int kHdrSdrRatioOverlayEnable = 1;
+static constexpr int kHdrSdrRatioOverlayDisable = 0;
+static constexpr int kHdrSdrRatioOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowHdrSdrRatioPreferenceController
+static_assert(kHdrSdrRatioOverlayCode == 1043);
+static_assert(kHdrSdrRatioOverlayEnable == 1);
+static_assert(kHdrSdrRatioOverlayDisable == 0);
+static_assert(kHdrSdrRatioOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    Parcel request;
+    request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+    request.writeInt32(command);
+    ASSERT_EQ(NO_ERROR,
+              IInterface::asBinder(sf)->transact(kHdrSdrRatioOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+    Parcel reply;
+    sendCommandToSf(kHdrSdrRatioOverlayQuery, reply);
+    return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+    static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+    static constexpr auto kIterations = 10;
+    for (int i = 0; i < kIterations; i++) {
+        if (enabled == isOverlayEnabled()) {
+            return;
+        }
+        std::this_thread::sleep_for(kTimeout / kIterations);
+    }
+}
+
+void toggleOverlay(bool enabled) {
+    if (enabled == isOverlayEnabled()) {
+        return;
+    }
+
+    Parcel reply;
+    const auto command = enabled ? kHdrSdrRatioOverlayEnable : kHdrSdrRatioOverlayDisable;
+    sendCommandToSf(command, reply);
+    waitForOverlay(enabled);
+    ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(HdrSdrRatioOverlayTest, enableAndDisableOverlay) {
+    toggleOverlay(true);
+    toggleOverlay(false);
+
+    toggleOverlay(true);
+    toggleOverlay(false);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 013694f..96cc333 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 
 #include <private/android_filesystem_config.h>
+#include <ui/DisplayState.h>
 
 #include "LayerTransactionTest.h"
 
@@ -32,11 +33,11 @@
 
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-        ASSERT_FALSE(display == nullptr);
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        ASSERT_FALSE(mDisplayToken == nullptr);
 
         ui::DisplayMode mode;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
         const ui::Size& resolution = mode.resolution;
 
         mDisplaySize = resolution;
@@ -57,7 +58,7 @@
         TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
+            t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
@@ -71,11 +72,18 @@
         LayerTransactionTest::TearDown();
         mBGSurfaceControl = 0;
         mFGSurfaceControl = 0;
+
+        // Restore display rotation
+        asTransaction([&](Transaction& t) {
+            Rect displayBounds{mDisplaySize};
+            t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
+        });
     }
 
     sp<SurfaceControl> mBGSurfaceControl;
     sp<SurfaceControl> mFGSurfaceControl;
     std::unique_ptr<ScreenCapture> mCapture;
+    sp<IBinder> mDisplayToken;
     ui::Size mDisplaySize;
 };
 
@@ -870,6 +878,42 @@
     mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
+TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
+    asTransaction([&](Transaction& t) {
+        Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
+        t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
+    });
+
+    DisplayCaptureArgs displayCaptureArgs;
+    displayCaptureArgs.displayToken = mDisplayToken;
+    displayCaptureArgs.width = mDisplaySize.width;
+    displayCaptureArgs.height = mDisplaySize.height;
+    displayCaptureArgs.useIdentityTransform = true;
+    ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
+
+    mCapture->expectBGColor(0, 0);
+    mCapture->expectFGColor(mDisplaySize.width - 65, 65);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
+    asTransaction([&](Transaction& t) {
+        Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
+        t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
+    });
+
+    DisplayCaptureArgs displayCaptureArgs;
+    displayCaptureArgs.displayToken = mDisplayToken;
+    displayCaptureArgs.width = mDisplaySize.width;
+    displayCaptureArgs.height = mDisplaySize.height;
+    displayCaptureArgs.useIdentityTransform = true;
+    ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
+
+    std::this_thread::sleep_for(std::chrono::seconds{5});
+
+    mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
+    mCapture->expectFGColor(65, mDisplaySize.height - 65);
+}
+
 TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 5fed9b4..91c6239 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -634,31 +634,6 @@
     expectVSyncCallbackScheduleReceived(false);
 }
 
-TEST_F(EventThreadTest, tracksEventConnections) {
-    setupEventThread(VSYNC_PERIOD);
-
-    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
-    ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
-    sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
-    mThread->setVsyncRate(1, errorConnection);
-    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
-    ConnectionEventRecorder secondConnectionEventRecorder{0};
-    sp<MockEventThreadConnection> secondConnection =
-            createConnection(secondConnectionEventRecorder);
-    mThread->setVsyncRate(1, secondConnection);
-    EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
-
-    // EventThread should enable vsync callbacks.
-    expectVSyncCallbackScheduleReceived(true);
-
-    // The first event will be seen by the connection, which then returns an error.
-    onVSyncEvent(123, 456, 789);
-    expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
-    expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
-                                         1u);
-    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
-}
-
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
     setupEventThread(VSYNC_PERIOD);
 
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index da00377..ec8069d 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -53,6 +53,7 @@
 using Hwc2::Config;
 
 using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+using hal::IComposerClient;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::ElementsAreArray;
@@ -119,6 +120,155 @@
     }
 }
 
+TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) {
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    constexpr hal::HWConfigId kConfigId = 42;
+
+    expectHotplugConnect(kHwcDisplayId);
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+    {
+        EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+                .WillOnce(Return(HalError::BAD_DISPLAY));
+        EXPECT_TRUE(mHwc.getModes(info->id).empty());
+    }
+    {
+        constexpr int32_t kWidth = 480;
+        constexpr int32_t kHeight = 720;
+        constexpr int32_t kConfigGroup = 1;
+        constexpr int32_t kVsyncPeriod = 16666667;
+
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+                                        _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::CONFIG_GROUP, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+        // Optional Parameters UNSUPPORTED
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+                                        _))
+                .WillOnce(Return(HalError::UNSUPPORTED));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+                                        _))
+                .WillOnce(Return(HalError::UNSUPPORTED));
+
+        EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+                                      Return(HalError::NONE)));
+
+        auto modes = mHwc.getModes(info->id);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        EXPECT_EQ(modes.front().dpiX, -1);
+        EXPECT_EQ(modes.front().dpiY, -1);
+
+        // Optional parameters are supported
+        constexpr int32_t kDpi = 320;
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+                                        _))
+                .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+                                        _))
+                .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+        modes = mHwc.getModes(info->id);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        // DPI values are scaled by 1000 in the legacy implementation.
+        EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+        EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+    }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    constexpr hal::HWConfigId kConfigId = 42;
+    expectHotplugConnect(kHwcDisplayId);
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true));
+
+    {
+        EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _))
+                .WillOnce(Return(HalError::BAD_DISPLAY));
+        EXPECT_TRUE(mHwc.getModes(info->id).empty());
+    }
+    {
+        constexpr int32_t kWidth = 480;
+        constexpr int32_t kHeight = 720;
+        constexpr int32_t kConfigGroup = 1;
+        constexpr int32_t kVsyncPeriod = 16666667;
+        hal::DisplayConfiguration displayConfiguration;
+        displayConfiguration.configId = kConfigId;
+        displayConfiguration.configGroup = kConfigGroup;
+        displayConfiguration.height = kHeight;
+        displayConfiguration.width = kWidth;
+        displayConfiguration.vsyncPeriod = kVsyncPeriod;
+
+        EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<hal::DisplayConfiguration>{
+                                        displayConfiguration}),
+                                Return(HalError::NONE)));
+
+        // Optional dpi not supported
+        auto modes = mHwc.getModes(info->id);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        EXPECT_EQ(modes.front().dpiX, -1);
+        EXPECT_EQ(modes.front().dpiY, -1);
+
+        // Supports optional dpi parameter
+        constexpr int32_t kDpi = 320;
+        displayConfiguration.dpi = {kDpi, kDpi};
+
+        EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<hal::DisplayConfiguration>{
+                                        displayConfiguration}),
+                                Return(HalError::NONE)));
+
+        modes = mHwc.getModes(info->id);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        EXPECT_EQ(modes.front().dpiX, kDpi);
+        EXPECT_EQ(modes.front().dpiY, kDpi);
+    }
+}
+
 TEST_F(HWComposerTest, onVsync) {
     constexpr hal::HWDisplayId kHwcDisplayId = 1;
     expectHotplugConnect(kHwcDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index fab3c0e..3200003 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;
@@ -149,10 +158,6 @@
 
     EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
     mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
-
-    static constexpr size_t kEventConnections = 5;
-    EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
-    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
 TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
@@ -199,7 +204,8 @@
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
-    mScheduler->chooseRefreshRateForContent();
+    mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+                                            /*updateAttachedChoreographer*/ false);
 }
 
 TEST_F(SchedulerTest, updateDisplayModes) {
@@ -278,11 +284,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) {
@@ -437,4 +445,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_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index e176546..703bdda 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -49,9 +49,17 @@
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
         mFlinger.configureAndCommit();
 
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
+
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
+                .WillRepeatedly(Return(
+                        TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
                            .setRefreshRateSelector(std::move(selectorPtr))
-                           .inject();
+                           .inject(std::move(vsyncController), std::move(vsyncTracker));
 
         // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
         // will call setActiveConfig instead of setActiveConfigWithConstraints.
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 156c40a..909b8b8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -55,6 +55,12 @@
 #include "mock/MockSchedulerCallback.h"
 #include "mock/system/window/MockNativeWindow.h"
 
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
 namespace android {
 
 struct DisplayStatInfo;
@@ -624,6 +630,8 @@
 
     auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
 
+    auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+
     auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
 
     ~TestableSurfaceFlinger() {
@@ -634,6 +642,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>();
@@ -908,6 +917,13 @@
         }
 
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
+            return inject(std::make_unique<mock::VsyncController>(),
+                          std::make_shared<mock::VSyncTracker>());
+        }
+
+        sp<DisplayDevice> inject(std::unique_ptr<android::scheduler::VsyncController> controller,
+                                 std::shared_ptr<android::scheduler::VSyncTracker> tracker)
+                NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
             auto& modes = mDisplayModes;
@@ -972,7 +988,9 @@
 
                 if (mFlinger.scheduler() && mRegisterDisplay) {
                     mFlinger.scheduler()->registerDisplay(physicalId,
-                                                          display->holdRefreshRateSelector());
+                                                          display->holdRefreshRateSelector(),
+                                                          std::move(controller),
+                                                          std::move(tracker));
                 }
 
                 display->setActiveMode(activeModeId, fps, fps);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a9ae1d3..86ed233 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1079,7 +1079,6 @@
     constexpr size_t TOTAL_FRAMES = 5;
     constexpr size_t MISSED_FRAMES = 4;
     constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
-    constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
     constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
     constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
     constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
@@ -1100,7 +1099,6 @@
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
 
-    mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
     mTimeStats->setPowerMode(PowerMode::ON);
     mTimeStats->recordFrameDuration(1000000, 3000000);
     mTimeStats->recordRenderEngineDuration(2000000, 4000000);
@@ -1157,7 +1155,6 @@
     EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
     // Display on millis is not checked.
     EXPECT_EQ(atom.animation_millis(), 2);
-    EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
     EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
     EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
     EXPECT_EQ(atom.total_timeline_frames(), 9);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index d3fb9fc..8d48940 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -51,6 +51,7 @@
     ~Composer() override;
 
     MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
+    MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override));
     MOCK_METHOD0(getCapabilities,
                  std::vector<aidl::android::hardware::graphics::composer3::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
@@ -70,6 +71,7 @@
     MOCK_METHOD4(getDisplayAttribute,
                  Error(Display, Config config, IComposerClient::Attribute, int32_t*));
     MOCK_METHOD2(getDisplayConfigs, Error(Display, std::vector<Config>*));
+    MOCK_METHOD2(getDisplayConfigurations, Error(Display, std::vector<DisplayConfiguration>*));
     MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
     MOCK_METHOD4(getDisplayRequests,
                  Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8d57049..9a1a16d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -49,7 +49,6 @@
                 (const sp<android::EventThreadConnection>&), (const, override));
     MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
     MOCK_METHOD(void, pauseVsyncCallback, (bool));
-    MOCK_METHOD(size_t, getEventThreadConnectionCount, (), (override));
     MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (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/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 86fbadc..c82e45b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -34,7 +34,6 @@
     MOCK_METHOD0(incrementTotalFrames, void());
     MOCK_METHOD0(incrementMissedFrames, void());
     MOCK_METHOD0(incrementRefreshRateSwitches, void());
-    MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index b73d2cb..114f863 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,6 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <android/hardware/graphics/common/1.0/types.h>
 #include <grallocusage/GrallocUsageConversion.h>
 #include <graphicsenv/GraphicsEnv.h>
@@ -25,8 +26,6 @@
 #include <sync/sync.h>
 #include <system/window.h>
 #include <ui/BufferQueueDefs.h>
-#include <ui/DebugUtils.h>
-#include <ui/PixelFormat.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
@@ -37,6 +36,7 @@
 
 #include "driver.h"
 
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
 using android::hardware::graphics::common::V1_0::BufferUsage;
 
 namespace vulkan {
@@ -503,27 +503,27 @@
     *count = num_copied;
 }
 
-android::PixelFormat GetNativePixelFormat(VkFormat format) {
-    android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888;
+PixelFormat GetNativePixelFormat(VkFormat format) {
+    PixelFormat native_format = PixelFormat::RGBA_8888;
     switch (format) {
         case VK_FORMAT_R8G8B8A8_UNORM:
         case VK_FORMAT_R8G8B8A8_SRGB:
-            native_format = android::PIXEL_FORMAT_RGBA_8888;
+            native_format = PixelFormat::RGBA_8888;
             break;
         case VK_FORMAT_R5G6B5_UNORM_PACK16:
-            native_format = android::PIXEL_FORMAT_RGB_565;
+            native_format = PixelFormat::RGB_565;
             break;
         case VK_FORMAT_R16G16B16A16_SFLOAT:
-            native_format = android::PIXEL_FORMAT_RGBA_FP16;
+            native_format = PixelFormat::RGBA_FP16;
             break;
         case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
-            native_format = android::PIXEL_FORMAT_RGBA_1010102;
+            native_format = PixelFormat::RGBA_1010102;
             break;
         case VK_FORMAT_R8_UNORM:
-            native_format = android::PIXEL_FORMAT_R_8;
+            native_format = PixelFormat::R_8;
             break;
         case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
-            native_format = android::PIXEL_FORMAT_RGBA_10101010;
+            native_format = PixelFormat::RGBA_10101010;
             break;
         default:
             ALOGV("unsupported swapchain format %d", format);
@@ -1361,7 +1361,7 @@
     if (!allocator)
         allocator = &GetData(device).allocator;
 
-    android::PixelFormat native_pixel_format =
+    PixelFormat native_pixel_format =
         GetNativePixelFormat(create_info->imageFormat);
     android_dataspace native_dataspace =
         GetNativeDataspace(create_info->imageColorSpace);
@@ -1462,10 +1462,11 @@
 
     const auto& dispatch = GetData(device).driver;
 
-    err = native_window_set_buffers_format(window, native_pixel_format);
+    err = native_window_set_buffers_format(
+        window, static_cast<int>(native_pixel_format));
     if (err != android::OK) {
         ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)",
-              decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err);
+              toString(native_pixel_format).c_str(), strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }