Merge "SF: Identify layers with buffers when dumping offscreen list"
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
 
 cc_library_headers {
     name: "libandroid_sensor_headers",
-    vendor: true,
+    vendor_available: true,
+    host_supported: true,
     export_include_dirs: ["include_sensor"],
 }
 
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 33dceff..69a1df2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -194,6 +194,8 @@
 static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
 static const std::string ANR_DIR = "/data/anr/";
 static const std::string ANR_FILE_PREFIX = "anr_";
+static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
+static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
 
 // TODO: temporary variables and functions used during C++ refactoring
 
@@ -1110,6 +1112,16 @@
     RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
+static void DumpShutdownCheckpoints() {
+    const bool shutdown_checkpoints_dumped = AddDumps(
+        ds.shutdown_checkpoints_.begin(), ds.shutdown_checkpoints_.end(),
+        "SHUTDOWN CHECKPOINTS", false /* add_to_zip */);
+    if (!shutdown_checkpoints_dumped) {
+        printf("*** NO SHUTDOWN CHECKPOINTS to dump in %s\n\n",
+            SHUTDOWN_CHECKPOINTS_DIR.c_str());
+    }
+}
+
 static void DumpDynamicPartitionInfo() {
     if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
         return;
@@ -1704,6 +1716,8 @@
 
     DoKmsg();
 
+    DumpShutdownCheckpoints();
+
     DumpIpAddrAndRules();
 
     dump_route_tables();
@@ -1861,6 +1875,8 @@
     if (!PropertiesHelper::IsDryRun()) {
         ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX);
         ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
+        ds.shutdown_checkpoints_ = GetDumpFds(
+            SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX);
     }
 
     ds.AddDir(RECOVERY_DIR, true);
@@ -2915,6 +2931,7 @@
     }
     tombstone_data_.clear();
     anr_data_.clear();
+    shutdown_checkpoints_.clear();
 
     // Instead of shutdown the pool, we delete temporary files directly since
     // shutdown blocking the call.
@@ -3202,6 +3219,7 @@
 
     tombstone_data_.clear();
     anr_data_.clear();
+    shutdown_checkpoints_.clear();
 
     return (consent_callback_ != nullptr &&
             consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 5f3acfd..9f894b5 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -514,6 +514,9 @@
     // List of open ANR dump files.
     std::vector<DumpData> anr_data_;
 
+    // List of open shutdown checkpoint files.
+    std::vector<DumpData> shutdown_checkpoints_;
+
     // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
     std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b1283eb..bb6639e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -501,10 +501,6 @@
 }
 
 static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
-    if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
-        return true;
-    }
-
     int32_t uid = multiuser_get_uid(userId, appId);
     int shared_app_gid = multiuser_get_shared_gid(userId, appId);
     if (shared_app_gid == -1) {
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 105f952..085fc27 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -601,12 +601,15 @@
     float accuracy;
 } AHeadingEvent;
 
+// LINT.IfChange
 /**
  * Information that describes a sensor event, refer to
  * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
  * documentation.
+ *
+ * NOTE: changes to this struct has to be backward compatible and reflected in
+ * sensors_event_t
  */
-/* NOTE: changes to this struct has to be backward compatible */
 typedef struct ASensorEvent {
     int32_t version; /* sizeof(struct ASensorEvent) */
     int32_t sensor;  /** The sensor that generates this event */
@@ -651,6 +654,7 @@
     uint32_t flags;
     int32_t reserved1[3];
 } ASensorEvent;
+// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
 
 struct ASensorManager;
 /**
diff --git a/include/ftl/details/mixins.h b/include/ftl/details/mixins.h
new file mode 100644
index 0000000..9ab9e08
--- /dev/null
+++ b/include/ftl/details/mixins.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 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
+
+namespace android::ftl::details {
+
+template <typename Self, template <typename> class>
+class Mixin {
+ protected:
+  constexpr Self& self() { return *static_cast<Self*>(this); }
+  constexpr const Self& self() const { return *static_cast<const Self*>(this); }
+
+  constexpr auto& mut() { return self().value_; }
+};
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index 82af1d6..075d12b 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -92,7 +92,7 @@
 //   enum class E { A, B, C };
 //   static_assert(ftl::to_underlying(E::B) == 1);
 //
-template <typename E>
+template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
 constexpr auto to_underlying(E v) {
   return static_cast<std::underlying_type_t<E>>(v);
 }
diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h
new file mode 100644
index 0000000..0e1d200
--- /dev/null
+++ b/include/ftl/mixins.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2022 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 <ftl/details/mixins.h>
+
+namespace android::ftl {
+
+// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common
+// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must
+// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic,
+// etc.) are enabled through inheritance:
+//
+//   struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+//     using Constructible::Constructible;
+//   };
+//
+//   static_assert(!std::is_default_constructible_v<Id>);
+//
+// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is
+// zero-initialized unless specified:
+//
+//   struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+//                  ftl::Equatable<Color>,
+//                  ftl::Orderable<Color> {
+//     using DefaultConstructible::DefaultConstructible;
+//   };
+//
+//   static_assert(Color() == Color(0u));
+//   static_assert(ftl::to_underlying(Color(-1)) == 255u);
+//   static_assert(Color(1u) < Color(2u));
+//
+//   struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+//                     ftl::Equatable<Sequence>,
+//                     ftl::Orderable<Sequence>,
+//                     ftl::Incrementable<Sequence> {
+//     using DefaultConstructible::DefaultConstructible;
+//   };
+//
+//   static_assert(Sequence() == Sequence(-1));
+//
+// The underlying type need not be a fundamental type:
+//
+//   struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+//                    ftl::Equatable<Timeout>,
+//                    ftl::Addable<Timeout> {
+//     using DefaultConstructible::DefaultConstructible;
+//   };
+//
+//   using namespace std::chrono_literals;
+//   static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+//
+template <typename Self, typename T>
+struct Constructible {
+  explicit constexpr Constructible(T value) : value_(value) {}
+
+  explicit constexpr operator const T&() const { return value_; }
+
+ private:
+  template <typename, template <typename> class>
+  friend class details::Mixin;
+
+  T value_;
+};
+
+template <typename Self, typename T, auto kDefault = T{}>
+struct DefaultConstructible : Constructible<Self, T> {
+  using Constructible<Self, T>::Constructible;
+  constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {}
+};
+
+// Shorthand for casting a type-safe wrapper to its underlying value.
+template <typename Self, typename T>
+constexpr const T& to_underlying(const Constructible<Self, T>& c) {
+  return static_cast<const T&>(c);
+}
+
+// Comparison operators for equality.
+template <typename Self>
+struct Equatable : details::Mixin<Self, Equatable> {
+  constexpr bool operator==(const Self& other) const {
+    return to_underlying(this->self()) == to_underlying(other);
+  }
+
+  constexpr bool operator!=(const Self& other) const { return !(*this == other); }
+};
+
+// Comparison operators for ordering.
+template <typename Self>
+struct Orderable : details::Mixin<Self, Orderable> {
+  constexpr bool operator<(const Self& other) const {
+    return to_underlying(this->self()) < to_underlying(other);
+  }
+
+  constexpr bool operator>(const Self& other) const { return other < this->self(); }
+  constexpr bool operator>=(const Self& other) const { return !(*this < other); }
+  constexpr bool operator<=(const Self& other) const { return !(*this > other); }
+};
+
+// Pre-increment and post-increment operators.
+template <typename Self>
+struct Incrementable : details::Mixin<Self, Incrementable> {
+  constexpr Self& operator++() {
+    ++this->mut();
+    return this->self();
+  }
+
+  constexpr Self operator++(int) {
+    const Self tmp = this->self();
+    operator++();
+    return tmp;
+  }
+};
+
+// Additive operators, including incrementing.
+template <typename Self>
+struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> {
+  constexpr Self& operator+=(const Self& other) {
+    this->mut() += to_underlying(other);
+    return this->self();
+  }
+
+  constexpr Self operator+(const Self& other) const {
+    Self tmp = this->self();
+    return tmp += other;
+  }
+
+ private:
+  using Base = details::Mixin<Self, Addable>;
+  using Base::mut;
+  using Base::self;
+};
+
+}  // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 626507f..a818128 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -95,8 +95,26 @@
     if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
     return R();
   }
+
+  // Delete new for this class. Its base doesn't have a virtual destructor, and
+  // if it got deleted via base class pointer, it would cause undefined
+  // behavior. There's not a good reason to allocate this object on the heap
+  // anyway.
+  static void* operator new(size_t) = delete;
+  static void* operator new[](size_t) = delete;
+
 };
 
+template <typename T, typename U>
+constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+  return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  return !(lhs == rhs);
+}
+
 // Deduction guides.
 template <typename T>
 Optional(T) -> Optional<T>;
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 98a18c9..7457496 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -21,6 +21,7 @@
 #include <ftl/string.h>
 #include <gui/constants.h>
 #include <input/Input.h>
+#include <ui/Rotation.h>
 
 #include <cinttypes>
 #include <optional>
@@ -29,13 +30,6 @@
 
 namespace android {
 
-enum {
-    DISPLAY_ORIENTATION_0 = 0,
-    DISPLAY_ORIENTATION_90 = 1,
-    DISPLAY_ORIENTATION_180 = 2,
-    DISPLAY_ORIENTATION_270 = 3
-};
-
 /**
  * Describes the different type of viewports supported by input flinger.
  * Keep in sync with values in InputManagerService.java.
@@ -54,7 +48,7 @@
  */
 struct DisplayViewport {
     int32_t displayId; // -1 if invalid
-    int32_t orientation;
+    ui::Rotation orientation;
     int32_t logicalLeft;
     int32_t logicalTop;
     int32_t logicalRight;
@@ -74,7 +68,7 @@
 
     DisplayViewport()
           : displayId(ADISPLAY_ID_NONE),
-            orientation(DISPLAY_ORIENTATION_0),
+            orientation(ui::ROTATION_0),
             logicalLeft(0),
             logicalTop(0),
             logicalRight(0),
@@ -111,7 +105,7 @@
 
     void setNonDisplayViewport(int32_t width, int32_t height) {
         displayId = ADISPLAY_ID_NONE;
-        orientation = DISPLAY_ORIENTATION_0;
+        orientation = ui::ROTATION_0;
         logicalLeft = 0;
         logicalTop = 0;
         logicalRight = width;
diff --git a/include/input/Input.h b/include/input/Input.h
index d298d81..015efdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -577,7 +577,7 @@
 
     inline const ui::Transform& getTransform() const { return mTransform; }
 
-    int getSurfaceRotation() const;
+    std::optional<ui::Rotation> getSurfaceRotation() const;
 
     inline float getXPrecision() const { return mXPrecision; }
 
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dc928b8..867a089 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -125,14 +125,21 @@
     bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
             Vector<KeyEvent>& outEvents) const;
 
+    /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
+     * and usageCodes are mapped to corresponding Android Keycode */
+    void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
     /* Maps a scan code and usage code to a key code, in case this key map overrides
      * the mapping in some way. */
     status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
 
-    /* Tries to find a replacement key code for a given key code and meta state
-     * in character map. */
-    void tryRemapKey(int32_t scanCode, int32_t metaState,
-            int32_t* outKeyCode, int32_t* outMetaState) const;
+    /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
+    int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+
+    /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
+     * that tries to find a replacement key code based on current meta state */
+    std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
+                                                                           int32_t metaState) const;
 
 #ifdef __linux__
     /* Reads a key map from a parcel. */
@@ -227,8 +234,9 @@
     std::string mLoadFileName;
     bool mLayoutOverlayApplied;
 
-    KeyedVector<int32_t, int32_t> mKeysByScanCode;
-    KeyedVector<int32_t, int32_t> mKeysByUsageCode;
+    std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
+    std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
+    std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode;
 
     KeyCharacterMap(const std::string& filename);
 
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index a616a95..1e4f6e7 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <ui/Rotation.h>
+
 #include <stdint.h>
 #include <sys/time.h>
 #include <vector>
@@ -58,7 +60,7 @@
      * Rotate the video frame.
      * The rotation value is an enum from ui/Rotation.h
      */
-    void rotate(int32_t orientation);
+    void rotate(ui::Rotation orientation);
 
 private:
     uint32_t mHeight;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 11c8e5d..c0f3e30 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -45,11 +45,11 @@
 
 #define IF_LOG_TRANSACTIONS() if (false)
 #define IF_LOG_COMMANDS() if (false)
-#define LOG_REMOTEREFS(...) 
+#define LOG_REMOTEREFS(...)
 #define IF_LOG_REMOTEREFS() if (false)
 
-#define LOG_THREADPOOL(...) 
-#define LOG_ONEWAY(...) 
+#define LOG_THREADPOOL(...)
+#define LOG_ONEWAY(...)
 
 #else
 
@@ -394,14 +394,92 @@
     // context, so we don't abort
 }
 
+constexpr uint32_t encodeExplicitIdentity(bool hasExplicitIdentity, pid_t callingPid) {
+    uint32_t as_unsigned = static_cast<uint32_t>(callingPid);
+    if (hasExplicitIdentity) {
+        return as_unsigned | (1 << 30);
+    } else {
+        return as_unsigned & ~(1 << 30);
+    }
+}
+
+constexpr int64_t packCallingIdentity(bool hasExplicitIdentity, uid_t callingUid,
+                                      pid_t callingPid) {
+    // Calling PID is a 32-bit signed integer, but doesn't consume the entire 32 bit space.
+    // To future-proof this and because we have extra capacity, we decided to also support -1,
+    // since this constant is used to represent invalid UID in other places of the system.
+    // Thus, we pack hasExplicitIdentity into the 2nd bit from the left.  This allows us to
+    // preserve the (left-most) bit for the sign while also encoding the value of
+    // hasExplicitIdentity.
+    //               32b     |        1b         |         1b            |        30b
+    // token = [ calling uid | calling pid(sign) | has explicit identity | calling pid(rest) ]
+    uint64_t token = (static_cast<uint64_t>(callingUid) << 32) |
+            encodeExplicitIdentity(hasExplicitIdentity, callingPid);
+    return static_cast<int64_t>(token);
+}
+
+constexpr bool unpackHasExplicitIdentity(int64_t token) {
+    return static_cast<int32_t>(token) & (1 << 30);
+}
+
+constexpr uid_t unpackCallingUid(int64_t token) {
+    return static_cast<uid_t>(token >> 32);
+}
+
+constexpr pid_t unpackCallingPid(int64_t token) {
+    int32_t encodedPid = static_cast<int32_t>(token);
+    if (encodedPid & (1 << 31)) {
+        return encodedPid | (1 << 30);
+    } else {
+        return encodedPid & ~(1 << 30);
+    }
+}
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, 9999)) == true,
+              "pack true hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, 9999)) == 1000, "pack true uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, 9999)) == 9999, "pack true pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, 9999)) == false,
+              "pack false hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, 9999)) == 1000, "pack false uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, 9999)) == 9999, "pack false pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, -1)) == true,
+              "pack true (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, -1)) == 1000,
+              "pack true (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, -1)) == -1,
+              "pack true (negative) pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, -1)) == false,
+              "pack false (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, -1)) == 1000,
+              "pack false (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, -1)) == -1,
+              "pack false (negative) pid");
+
 int64_t IPCThreadState::clearCallingIdentity()
 {
     // ignore mCallingSid for legacy reasons
-    int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
+    int64_t token = packCallingIdentity(mHasExplicitIdentity, mCallingUid, mCallingPid);
     clearCaller();
+    mHasExplicitIdentity = true;
     return token;
 }
 
+bool IPCThreadState::hasExplicitIdentity() {
+    return mHasExplicitIdentity;
+}
+
 void IPCThreadState::setStrictModePolicy(int32_t policy)
 {
     mStrictModePolicy = policy;
@@ -474,9 +552,10 @@
 
 void IPCThreadState::restoreCallingIdentity(int64_t token)
 {
-    mCallingUid = (int)(token>>32);
+    mCallingUid = unpackCallingUid(token);
     mCallingSid = nullptr;  // not enough data to restore
-    mCallingPid = (int)token;
+    mCallingPid = unpackCallingPid(token);
+    mHasExplicitIdentity = unpackHasExplicitIdentity(token);
 }
 
 void IPCThreadState::clearCaller()
@@ -889,6 +968,7 @@
         mCallRestriction(mProcess->mCallRestriction) {
     pthread_setspecific(gTLS, this);
     clearCaller();
+    mHasExplicitIdentity = false;
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -937,6 +1017,10 @@
             if (!reply && !acquireResult) goto finish;
             break;
 
+        case BR_TRANSACTION_PENDING_FROZEN:
+            ALOGW("Sending oneway calls to frozen process.");
+            goto finish;
+
         case BR_DEAD_REPLY:
             err = DEAD_OBJECT;
             goto finish;
@@ -1279,6 +1363,7 @@
             const pid_t origPid = mCallingPid;
             const char* origSid = mCallingSid;
             const uid_t origUid = mCallingUid;
+            const bool origHasExplicitIdentity = mHasExplicitIdentity;
             const int32_t origStrictModePolicy = mStrictModePolicy;
             const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
             const int32_t origWorkSource = mWorkSource;
@@ -1292,6 +1377,7 @@
             mCallingPid = tr.sender_pid;
             mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
             mCallingUid = tr.sender_euid;
+            mHasExplicitIdentity = false;
             mLastTransactionBinderFlags = tr.flags;
 
             // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
@@ -1367,6 +1453,7 @@
             mCallingPid = origPid;
             mCallingSid = origSid;
             mCallingUid = origUid;
+            mHasExplicitIdentity = origHasExplicitIdentity;
             mStrictModePolicy = origStrictModePolicy;
             mLastTransactionBinderFlags = origTransactionBinderFlags;
             mWorkSource = origWorkSource;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a0c4334..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -444,7 +444,7 @@
     bool declared;
     if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
         !status.isOk()) {
-        ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+        ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
               status.toString8().c_str());
         return false;
     }
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 4b07608..ee081c4 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -614,11 +614,14 @@
                 if (status_t status = readInt32(&fdIndex); status != OK) {
                     return status;
                 }
-                const auto& oldFd = otherRpcFields->mFds->at(fdIndex);
+                int oldFd = toRawFd(otherRpcFields->mFds->at(fdIndex));
                 // To match kernel binder behavior, we always dup, even if the
                 // FD was unowned in the source parcel.
-                rpcFields->mFds->emplace_back(
-                        base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0)));
+                int newFd = -1;
+                if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) {
+                    ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
+                }
+                rpcFields->mFds->emplace_back(base::unique_fd(newFd));
                 // Fixup the index in the data.
                 mDataPos = newDataPos + 4;
                 if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..254dda8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -439,6 +439,10 @@
     return mCurrentThreads;
 }
 
+bool ProcessState::isThreadPoolStarted() const {
+    return mThreadPoolStarted;
+}
+
 #define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
 bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
     static const char* const names[] = {
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
 #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
 #endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
 
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
 #endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..8cc8105 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,7 +230,6 @@
         "android.graphicsenv.IGpuService",
         "android.gui.IConsumerListener",
         "android.gui.IGraphicBufferConsumer",
-        "android.gui.ITransactionComposerListener",
         "android.gui.SensorEventConnection",
         "android.gui.SensorServer",
         "android.hardware.ICamera",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index c01e92f..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -139,12 +139,15 @@
             int64_t             clearCallingIdentity();
             // Restores PID/UID (not SID)
             void                restoreCallingIdentity(int64_t token);
+            bool hasExplicitIdentity();
 
+            // For main functions - dangerous for libraries to use
             status_t            setupPolling(int* fd);
             status_t            handlePolledCommands();
             void                flushCommands();
             bool                flushIfNeeded();
 
+            // For main functions - dangerous for libraries to use
             void                joinThreadPool(bool isMain = true);
             
             // Stop the local process.
@@ -241,6 +244,7 @@
             bool                mPropagateWorkSource;
             bool                mIsLooper;
             bool mIsFlushing;
+            bool mHasExplicitIdentity;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
             CallRestriction     mCallRestriction;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..bad8cb1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,6 +50,7 @@
 
     sp<IBinder> getContextObject(const sp<IBinder>& caller);
 
+    // For main functions - dangerous for libraries to use
     void startThreadPool();
 
     bool becomeContextManager();
@@ -57,8 +58,10 @@
     sp<IBinder> getStrongProxyForHandle(int32_t handle);
     void expungeHandle(int32_t handle, IBinder* binder);
 
+    // TODO: deprecate.
     void spawnPooledThread(bool isMain);
 
+    // For main functions - dangerous for libraries to use
     status_t setThreadPoolMaxThreadCount(size_t maxThreads);
     status_t enableOnewaySpamDetection(bool enable);
     void giveThreadPoolName();
@@ -94,6 +97,11 @@
      */
     size_t getThreadPoolMaxTotalThreadCount() const;
 
+    /**
+     * Check to see if the thread pool has started.
+     */
+    bool isThreadPoolStarted() const;
+
     enum class DriverFeature {
         ONEWAY_SPAM_DETECTION,
         EXTENDED_ERROR,
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index dd177af..f08bde8 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -17,20 +17,41 @@
 #pragma once
 
 #include <sys/socket.h>
+#include <stdint.h>
 
 extern "C" {
 
 struct AIBinder;
+struct ARpcServer;
 
 // Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server and joins before returning.
-bool RunVsockRpcServer(AIBinder* service, unsigned int port);
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
 
-// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
-bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
-                               void (*readyCallback)(void* param), void* param);
+// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
+// and a given root IBinder object.
+// The socket should be created in init.rc with the same `name`.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);
+
+// Runs ARpcServer_join() in a background thread. Immediately returns.
+void ARpcServer_start(ARpcServer* server);
+
+// Joins the thread of a running RpcServer instance. At any given point, there
+// can only be one thread calling ARpcServer_join().
+// If a client needs to actively terminate join, call ARpcServer_shutdown() in
+// a separate thread.
+void ARpcServer_join(ARpcServer* server);
+
+// Shuts down any running ARpcServer_join().
+void ARpcServer_shutdown(ARpcServer* server);
+
+// Frees the ARpcServer handle and drops the reference count on the underlying
+// RpcServer instance. The handle must not be reused afterwards.
+// This automatically calls ARpcServer_shutdown().
+void ARpcServer_free(ARpcServer* server);
 
 // Starts an RPC server on a given port and a given root IBinder factory.
 // RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
@@ -42,15 +63,6 @@
 
 AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
 
-// Starts a Unix domain RPC server with a given init-managed Unix domain `name` and
-// a given root IBinder object.
-// The socket should be created in init.rc with the same `name`.
-//
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
-bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
-                                void (*readyCallback)(void* param), void* param);
-
 // Gets the service via the RPC binder with Unix domain socket with the given
 // Unix socket `name`.
 // The final Unix domain socket path name is /dev/socket/`name`.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 9edb3b6..f55c779 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <binder_rpc_unstable.hpp>
+
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <android/binder_libbinder.h>
@@ -25,23 +27,32 @@
 using android::OK;
 using android::RpcServer;
 using android::RpcSession;
+using android::sp;
 using android::status_t;
 using android::statusToString;
 using android::base::unique_fd;
 
-extern "C" {
+// Opaque handle for RpcServer.
+struct ARpcServer {};
 
-void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service,
-                  void (*readyCallback)(void* param), void* param) {
-    server->setRootObject(AIBinder_toPlatformBinder(service));
-
-    if (readyCallback) readyCallback(param);
-    server->join();
-
-    // Shutdown any open sessions since server failed.
-    (void)server->shutdown();
+static sp<RpcServer> toRpcServer(ARpcServer* handle) {
+    auto ref = reinterpret_cast<RpcServer*>(handle);
+    return sp<RpcServer>::fromExisting(ref);
 }
 
+static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) {
+    auto ref = server.get();
+    ref->incStrong(ref);
+    return reinterpret_cast<ARpcServer*>(ref);
+}
+
+static void freeRpcServerHandle(ARpcServer* handle) {
+    auto ref = reinterpret_cast<RpcServer*>(handle);
+    ref->decStrong(ref);
+}
+
+extern "C" {
+
 bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
                                   void* factoryContext, unsigned int port) {
     auto server = RpcServer::make();
@@ -64,20 +75,47 @@
     return true;
 }
 
-bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
-                               void (*readyCallback)(void* param), void* param) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
     auto server = RpcServer::make();
     if (status_t status = server->setupVsockServer(port); status != OK) {
         LOG(ERROR) << "Failed to set up vsock server with port " << port
                    << " error: " << statusToString(status).c_str();
-        return false;
+        return nullptr;
     }
-    RunRpcServer(server, service, readyCallback, param);
-    return true;
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    return createRpcServerHandle(server);
 }
 
-bool RunVsockRpcServer(AIBinder* service, unsigned int port) {
-    return RunVsockRpcServerCallback(service, port, nullptr, nullptr);
+ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
+    auto server = RpcServer::make();
+    auto fd = unique_fd(android_get_control_socket(name));
+    if (!fd.ok()) {
+        LOG(ERROR) << "Failed to get fd for the socket:" << name;
+        return nullptr;
+    }
+    if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
+        LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
+                   << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    return createRpcServerHandle(server);
+}
+
+void ARpcServer_start(ARpcServer* handle) {
+    toRpcServer(handle)->start();
+}
+
+void ARpcServer_join(ARpcServer* handle) {
+    toRpcServer(handle)->join();
+}
+
+void ARpcServer_shutdown(ARpcServer* handle) {
+    toRpcServer(handle)->shutdown();
+}
+
+void ARpcServer_free(ARpcServer* handle) {
+    freeRpcServerHandle(handle);
 }
 
 AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
@@ -90,23 +128,6 @@
     return AIBinder_fromPlatformBinder(session->getRootObject());
 }
 
-bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
-                                void (*readyCallback)(void* param), void* param) {
-    auto server = RpcServer::make();
-    auto fd = unique_fd(android_get_control_socket(name));
-    if (!fd.ok()) {
-        LOG(ERROR) << "Failed to get fd for the socket:" << name;
-        return false;
-    }
-    if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
-        LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
-                   << " error: " << statusToString(status).c_str();
-        return false;
-    }
-    RunRpcServer(server, service, readyCallback, param);
-    return true;
-}
-
 AIBinder* UnixDomainRpcClient(const char* name) {
     std::string pathname(name);
     pathname = ANDROID_SOCKET_DIR "/" + pathname;
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index f9c7bcf..1bc2416 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -1,9 +1,12 @@
 LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
   global:
-    RunVsockRpcServer;
-    RunVsockRpcServerCallback;
+    ARpcServer_free;
+    ARpcServer_join;
+    ARpcServer_newInitUnixDomain;
+    ARpcServer_newVsock;
+    ARpcServer_shutdown;
+    ARpcServer_start;
     VsockRpcClient;
-    RunInitUnixDomainRpcServer;
     UnixDomainRpcClient;
     RpcPreconnectedClient;
   local:
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 81975e7..9949de2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -196,6 +196,10 @@
 
     bool isRemote() override final { return false; }
 
+    static std::string makeServiceName(std::string_view instance) {
+        return INTERFACE::descriptor + ("/" + std::string(instance));
+    }
+
    protected:
     /**
      * This function should only be called by asBinder. Otherwise, there is a possibility of
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 6a25db2..2a00736 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -160,7 +160,7 @@
 template <typename _T>
 std::string ToString(const _T& t) {
     if constexpr (details::ToEmptyString<_T>::value) {
-        return "";
+        return "<unimplemented>";
     } else if constexpr (std::is_same_v<bool, _T>) {
         return t ? "true" : "false";
     } else if constexpr (std::is_same_v<char16_t, _T>) {
@@ -176,9 +176,11 @@
         return t;
 #ifdef HAS_NDK_INTERFACE
     } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
-        return (t.get() == nullptr) ? "(null)" : "";
+        std::stringstream ss;
+        ss << "binder:" << std::hex << t.get();
+        return ss.str();
     } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
-        return (t.get() == -1) ? "(null)" : "";
+        return "fd:" + std::to_string(t.get());
 #endif
 #ifdef HAS_STRING16
     } else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..1af2119 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -613,7 +613,8 @@
  *
  *  1. If the binder died, shortly after the call to onBinderDied.
  *  2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- *     AIBinder_DeathRecipient_delete.
+ *     AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ *     finish.
  *  3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
  *
  * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 36273bc..ad4188f 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -109,6 +109,67 @@
         __INTRODUCED_IN(31);
 
 /**
+ * Function to call when a service is registered. The instance is passed as well as
+ * ownership of the binder named 'registered'.
+ *
+ * WARNING: a lock is held when this method is called in order to prevent races with
+ * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when
+ * implementing this method to avoid deadlocks.
+ *
+ * \param instance instance name of service registered
+ * \param registered ownership-passed instance of service registered
+ * \param cookie data passed during registration for notifications
+ */
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+                                           void* cookie);
+
+/**
+ * Represents a registration to servicemanager which can be cleared anytime.
+ */
+struct AServiceManager_NotificationRegistration;
+
+/**
+ * Get notifications when a service is registered. If the service is already registered,
+ * you will immediately get a notification.
+ *
+ * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead.
+ * That API will wait synchronously, which is what you usually want in cases, including
+ * using some feature or during boot up. There is a history of bugs where waiting for
+ * notifications like this races with service startup. Also, when this API is used, a service
+ * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there
+ * is a history of this API being used to know when a service is up as a proxy for whethre
+ * that service should be started. This should only be used if you are intending to get
+ * ahold of the service as a client. For lazy services, whether a service is registered
+ * should not be used as a proxy for when it should be registered, which is only known
+ * by the real client.
+ *
+ * WARNING: if you use this API, you must also ensure that you check missing services are
+ * started and crash otherwise. If service failures are ignored, the system rots.
+ *
+ * \param instance name of service to wait for notifications about
+ * \param onRegister callback for when service is registered
+ * \param cookie data associated with this callback
+ *
+ * \return the token for this registration. Deleting this token will unregister.
+ */
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+                                                AServiceManager_onRegister onRegister, void* cookie)
+        __INTRODUCED_IN(34);
+
+/**
+ * Unregister for notifications and delete the object.
+ *
+ * After this method is called, the callback is guaranteed to no longer be invoked. This will block
+ * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately
+ * destroy the void* cookie that was registered when this method returns.
+ *
+ * \param notification object to dismiss
+ */
+void AServiceManager_NotificationRegistration_delete(
+        AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34);
+
+/**
  * Check if a service is declared (e.g. VINTF manifest).
  *
  * \param instance identifier of the service.
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..ffcad55 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,17 +28,33 @@
  *
  * When using this, it is expected that ABinderProcess_setupPolling and
  * ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
  */
 void ABinderProcess_startThreadPool();
 /**
  * This sets the maximum number of threads that can be started in the threadpool. By default, after
  * startThreadPool is called, this is 15. If it is called additional times, it will only prevent
  * the kernel from starting new threads and will not delete already existing threads.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
  */
 bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
 /**
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
+ */
+bool ABinderProcess_isThreadPoolStarted();
+/**
  * This adds the current thread to the threadpool. This may cause the threadpool to exceed the
  * maximum size.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
  */
 void ABinderProcess_joinThreadPool();
 
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
  * requirements associated with that higher stability level. For instance, a
  * VINTF stability binder is required to be in the VINTF manifest. This API
  * can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ *     auto binder = ...->asBinder();
+ *     AIBinder_forceDowngradeToVendorStability(binder.get());
+ *     doSomething(binder);
  */
 void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
 
@@ -79,6 +88,15 @@
  * requirements associated with that higher stability level. For instance, a
  * VINTF stability binder is required to be in the VINTF manifest. This API
  * can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ *     auto binder = ...->asBinder();
+ *     AIBinder_forceDowngradeToSystemStability(binder.get());
+ *     doSomething(binder);
  */
 void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
 
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 32ca564..54e4628 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -154,7 +154,10 @@
 
 LIBBINDER_NDK34 { # introduced=UpsideDownCake
   global:
+    ABinderProcess_isThreadPoolStarted; # systemapi llndk
     AServiceManager_getUpdatableApexName; # systemapi
+    AServiceManager_registerForServiceNotifications; # systemapi llndk
+    AServiceManager_NotificationRegistration_delete; # systemapi llndk
 };
 
 LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..bc6610e 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -31,6 +31,9 @@
 bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
     return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
 }
+bool ABinderProcess_isThreadPoolStarted() {
+    return ProcessState::self()->isThreadPoolStarted();
+}
 void ABinderProcess_joinThreadPool() {
     IPCThreadState::self()->joinThreadPool();
 }
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index a12d0e9..e107c83 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -28,6 +28,7 @@
 using ::android::IServiceManager;
 using ::android::sp;
 using ::android::status_t;
+using ::android::statusToString;
 using ::android::String16;
 using ::android::String8;
 
@@ -86,6 +87,67 @@
     AIBinder_incStrong(ret.get());
     return ret.get();
 }
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+                                           void* cookie);
+
+struct AServiceManager_NotificationRegistration
+    : public IServiceManager::LocalRegistrationCallback {
+    std::mutex m;
+    const char* instance = nullptr;
+    void* cookie = nullptr;
+    AServiceManager_onRegister onRegister = nullptr;
+
+    virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) {
+        std::lock_guard<std::mutex> l(m);
+        if (onRegister == nullptr) return;
+
+        CHECK_EQ(String8(smInstance), instance);
+
+        sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+        AIBinder_incStrong(ret.get());
+
+        onRegister(instance, ret.get(), cookie);
+    }
+
+    void clear() {
+        std::lock_guard<std::mutex> l(m);
+        instance = nullptr;
+        cookie = nullptr;
+        onRegister = nullptr;
+    }
+};
+
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+                                                AServiceManager_onRegister onRegister,
+                                                void* cookie) {
+    CHECK_NE(instance, nullptr);
+    CHECK_NE(onRegister, nullptr) << instance;
+    // cookie can be nullptr
+
+    auto cb = sp<AServiceManager_NotificationRegistration>::make();
+    cb->instance = instance;
+    cb->onRegister = onRegister;
+    cb->cookie = cookie;
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) {
+        LOG(ERROR) << "Failed to register for service notifications for " << instance << ": "
+                   << statusToString(res);
+        return nullptr;
+    }
+
+    cb->incStrong(nullptr);
+    return cb.get();
+}
+
+void AServiceManager_NotificationRegistration_delete(
+        AServiceManager_NotificationRegistration* notification) {
+    CHECK_NE(notification, nullptr);
+    notification->clear();
+    notification->decStrong(nullptr);
+}
+
 bool AServiceManager_isDeclared(const char* instance) {
     if (instance == nullptr) {
         return false;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index e221e4c..9d5ef68 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -254,6 +254,47 @@
     AIBinder_decStrong(binder);
 }
 
+struct ServiceData {
+    std::string instance;
+    ndk::SpAIBinder binder;
+
+    static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) {
+        ServiceData* d = reinterpret_cast<ServiceData*>(cookie);
+        d->instance = instance;
+        d->binder = ndk::SpAIBinder(binder);
+    }
+};
+
+TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) {
+    ServiceData data;
+    auto* notif = AServiceManager_registerForServiceNotifications(
+            "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data);
+    ASSERT_NE(notif, nullptr);
+
+    sleep(1);  // give us a chance to fail
+    AServiceManager_NotificationRegistration_delete(notif);
+
+    // checking after deleting to avoid needing a mutex over the data - otherwise
+    // in an environment w/ multiple threads, you would need to guard access
+    EXPECT_EQ(data.instance, "");
+    EXPECT_EQ(data.binder, nullptr);
+}
+
+TEST(NdkBinder, RegisterForServiceNotificationsExisting) {
+    ServiceData data;
+    auto* notif = AServiceManager_registerForServiceNotifications(
+            kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data);
+    ASSERT_NE(notif, nullptr);
+
+    sleep(1);  // give us a chance to fail
+    AServiceManager_NotificationRegistration_delete(notif);
+
+    // checking after deleting to avoid needing a mutex over the data - otherwise
+    // in an environment w/ multiple threads, you would need to guard access
+    EXPECT_EQ(data.instance, kExistingNonNdkService);
+    EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService)));
+}
+
 TEST(NdkBinder, UnimplementedDump) {
     sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
     ASSERT_NE(foo, nullptr);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 738d16a..afd414a 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -17,7 +17,6 @@
     rustlibs: [
         "libbinder_ndk_sys",
         "libdowncast_rs",
-        "liblazy_static",
         "liblibc",
     ],
     host_supported: true,
@@ -160,7 +159,6 @@
     rustlibs: [
         "libbinder_ndk_sys",
         "libdowncast_rs",
-        "liblazy_static",
         "liblibc",
     ],
 }
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 9771cc9..f70ebfc 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -19,6 +19,7 @@
         "libbinder_rpc_unstable_bindgen_sys",
         "libbinder_rs",
         "libdowncast_rs",
+        "libforeign_types",
         "liblibc",
         "liblog_rust",
     ],
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 89a49a4..1b719aa 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -23,6 +23,4 @@
     get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface,
     get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service,
 };
-pub use server::{
-    run_init_unix_domain_rpc_server, run_vsock_rpc_server, run_vsock_rpc_server_with_factory,
-};
+pub use server::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index b350a13..42f5567 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -18,114 +18,89 @@
     unstable_api::{AIBinder, AsNative},
     SpIBinder,
 };
+use binder_rpc_unstable_bindgen::ARpcServer;
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::io::{Error, ErrorKind};
 use std::{ffi::CString, os::raw, ptr::null_mut};
 
-/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
-/// port.
-///
-/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
-/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
-/// attempt to connect.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_vsock_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
-where
-    F: FnOnce(),
-{
-    let mut ready_notifier = ReadyNotifier(Some(on_ready));
-    ready_notifier.run_vsock_server(service, port)
+foreign_type! {
+    type CType = binder_rpc_unstable_bindgen::ARpcServer;
+    fn drop = binder_rpc_unstable_bindgen::ARpcServer_free;
+
+    /// A type that represents a foreign instance of RpcServer.
+    #[derive(Debug)]
+    pub struct RpcServer;
+    /// A borrowed RpcServer.
+    pub struct RpcServerRef;
 }
 
-/// Runs a binder RPC server, serving the supplied binder service implementation on the given
-/// socket file name. The socket should be initialized in init.rc with the same name.
-///
-/// If and when the server is ready for connections, `on_ready` is called to allow appropriate
-/// action to be taken - e.g. to notify clients that they may now attempt to connect.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_init_unix_domain_rpc_server<F>(
-    service: SpIBinder,
-    socket_name: &str,
-    on_ready: F,
-) -> bool
-where
-    F: FnOnce(),
-{
-    let mut ready_notifier = ReadyNotifier(Some(on_ready));
-    ready_notifier.run_init_unix_domain_server(service, socket_name)
-}
+/// SAFETY - The opaque handle can be cloned freely.
+unsafe impl Send for RpcServer {}
+/// SAFETY - The underlying C++ RpcServer class is thread-safe.
+unsafe impl Sync for RpcServer {}
 
-struct ReadyNotifier<F>(Option<F>)
-where
-    F: FnOnce();
-
-impl<F> ReadyNotifier<F>
-where
-    F: FnOnce(),
-{
-    fn run_vsock_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
+impl RpcServer {
+    /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+    /// vsock port.
+    pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
         let service = service.as_native_mut();
-        let param = self.as_void_ptr();
 
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
         // Plus the binder objects are threadsafe.
-        // RunVsockRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
-        // uses them before it returns, which is during the lifetime of `self`.
         unsafe {
-            binder_rpc_unstable_bindgen::RunVsockRpcServerCallback(
-                service,
-                port,
-                Some(Self::ready_callback),
-                param,
-            )
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
         }
     }
 
-    fn run_init_unix_domain_server(&mut self, mut service: SpIBinder, socket_name: &str) -> bool {
+    /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+    /// socket file name. The socket should be initialized in init.rc with the same name.
+    pub fn new_init_unix_domain(
+        mut service: SpIBinder,
+        socket_name: &str,
+    ) -> Result<RpcServer, Error> {
         let socket_name = match CString::new(socket_name) {
             Ok(s) => s,
             Err(e) => {
                 log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
-                return false;
+                return Err(Error::from(ErrorKind::InvalidInput));
             }
         };
         let service = service.as_native_mut();
-        let param = self.as_void_ptr();
 
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
         // Plus the binder objects are threadsafe.
-        // RunInitUnixDomainRpcServer does not retain a reference to `ready_callback` or `param`;
-        // it only uses them before it returns, which is during the lifetime of `self`.
         unsafe {
-            binder_rpc_unstable_bindgen::RunInitUnixDomainRpcServer(
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain(
                 service,
                 socket_name.as_ptr(),
-                Some(Self::ready_callback),
-                param,
-            )
+            ))
         }
     }
 
-    fn as_void_ptr(&mut self) -> *mut raw::c_void {
-        self as *mut _ as *mut raw::c_void
-    }
-
-    unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
-        // SAFETY: This is only ever called by `RunVsockRpcServerCallback`, within the lifetime of the
-        // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly
-        // aligned non-null pointer to an initialized instance).
-        let ready_notifier = param as *mut Self;
-        ready_notifier.as_mut().unwrap().notify()
-    }
-
-    fn notify(&mut self) {
-        if let Some(on_ready) = self.0.take() {
-            on_ready();
+    unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
+        if ptr.is_null() {
+            return Err(Error::new(ErrorKind::Other, "Failed to start server"));
         }
+        Ok(RpcServer::from_ptr(ptr))
+    }
+}
+
+impl RpcServerRef {
+    /// Starts a new background thread and calls join(). Returns immediately.
+    pub fn start(&self) {
+        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) {
+        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) {
+        unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) };
     }
 }
 
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index dee05d0..6f686fb 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -22,7 +22,6 @@
 use crate::proxy::SpIBinder;
 use crate::sys;
 
-use lazy_static::lazy_static;
 use std::convert::TryFrom;
 use std::ffi::{c_void, CStr, CString};
 use std::fs::File;
@@ -508,10 +507,8 @@
     _private: (),
 }
 
-lazy_static! {
-    // Count of how many LazyServiceGuard objects are in existence.
-    static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0);
-}
+// Count of how many LazyServiceGuard objects are in existence.
+static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
 
 impl LazyServiceGuard {
     /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index d2bfde1..a2d48b6 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -16,9 +16,9 @@
 
 use binder::binder_impl::BorrowedParcel;
 use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
-use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
-use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
-use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
 
 macro_rules! read_parcel_interface {
     ($data_type:ty) => {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 25b524f..f7498c4 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -120,6 +120,7 @@
     BINDER_LIB_TEST_CAN_GET_SID,
     BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
     BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+    BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
     BINDER_LIB_TEST_LOCK_UNLOCK,
     BINDER_LIB_TEST_PROCESS_LOCK,
     BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -1383,6 +1384,14 @@
     EXPECT_EQ(replyi, kKernelThreads + 1);
 }
 
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+    Parcel data, reply;
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+    EXPECT_TRUE(reply.readBool());
+}
+
 size_t epochMillis() {
     using std::chrono::duration_cast;
     using std::chrono::milliseconds;
@@ -1849,6 +1858,10 @@
                 reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+                reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_PROCESS_LOCK: {
                 m_blockMutex.lock();
                 return NO_ERROR;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 68a827b..02aa45f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -913,6 +913,33 @@
     EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
 }
 
+TEST_P(BinderRpc, AppendInvalidFd) {
+    auto proc = createRpcTestSocketServerProcess({
+            .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+            .serverSupportedFileDescriptorTransportModes =
+                    {RpcSession::FileDescriptorTransportMode::UNIX},
+    });
+
+    int badFd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0);
+    ASSERT_NE(badFd, -1);
+
+    // Close the file descriptor so it becomes invalid for dup
+    close(badFd);
+
+    Parcel p1;
+    p1.markForBinder(proc.rootBinder);
+    p1.writeInt32(3);
+    EXPECT_EQ(OK, p1.writeFileDescriptor(badFd, false));
+
+    Parcel pRaw;
+    pRaw.markForBinder(proc.rootBinder);
+    EXPECT_EQ(OK, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+    pRaw.setDataPosition(0);
+    EXPECT_EQ(3, pRaw.readInt32());
+    ASSERT_EQ(-1, pRaw.readFileDescriptor());
+}
+
 TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
     if constexpr (!kEnableSharedLibs) {
         GTEST_SKIP() << "Test disabled because Binder was built as a static library";
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 61a2412..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,14 @@
     host_supported: true,
     unstable: true,
     srcs: [
-        "EmptyParcelable.aidl",
-        "SingleDataParcelable.aidl",
-        "GenericDataParcelable.aidl",
+        "parcelables/EmptyParcelable.aidl",
+        "parcelables/SingleDataParcelable.aidl",
+        "parcelables/GenericDataParcelable.aidl",
     ],
     backend: {
         java: {
-            enabled: false,
+            enabled: true,
+            platform_apis: true,
         },
         rust: {
             enabled: true,
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..768fbe1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
 #define FUZZ_LOG_TAG "binder"
 
 #include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
 #include "util.h"
 
 #include <android-base/hex.h>
@@ -359,19 +359,19 @@
     },
     [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
-        EmptyParcelable emptyParcelable{};
+        parcelables::EmptyParcelable emptyParcelable{};
         status_t status = emptyParcelable.readFromParcel(&p);
         FUZZ_LOG() << " status: " << status;
     },
     [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
-        SingleDataParcelable singleDataParcelable;
+        parcelables::SingleDataParcelable singleDataParcelable;
         status_t status = singleDataParcelable.readFromParcel(&p);
         FUZZ_LOG() <<" status: " << status;
     },
     [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
         FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
-        GenericDataParcelable genericDataParcelable;
+        parcelables::GenericDataParcelable genericDataParcelable;
         status_t status = genericDataParcelable.readFromParcel(&p);
         FUZZ_LOG() <<" status: " << status;
     },
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..53e7de4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
 #define FUZZ_LOG_TAG "binder_ndk"
 
 #include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
 
 #include <android/binder_parcel_utils.h>
 #include <android/binder_parcelable_utils.h>
@@ -183,19 +183,19 @@
 
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
-            aidl::EmptyParcelable emptyParcelable;
+            aidl::parcelables::EmptyParcelable emptyParcelable;
             binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
-            aidl::SingleDataParcelable singleDataParcelable;
+            aidl::parcelables::SingleDataParcelable singleDataParcelable;
             binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
         [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
             FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
-            aidl::GenericDataParcelable genericDataParcelable;
+            aidl::parcelables::GenericDataParcelable genericDataParcelable;
             binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
             FUZZ_LOG() << "status: " << status;
         },
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 86461c8..8bef33f 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -37,7 +37,9 @@
     }
 
     while (provider.remaining_bytes() > 0) {
-        uint32_t code = provider.ConsumeIntegral<uint32_t>();
+        // Most of the AIDL services will have small set of transaction codes.
+        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+                                               : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
         uint32_t flags = provider.ConsumeIntegral<uint32_t>();
         Parcel data;
         // for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-parcelable SingleDataParcelable{
-   int data;
+package parcelables;
+parcelable EmptyParcelable {
 }
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..f1079e9 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package parcelables;
 
 parcelable GenericDataParcelable {
     int data;
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package parcelables;
 
 parcelable SingleDataParcelable{
    int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index edc695f..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,6 +73,11 @@
                                                                                 1));
                         CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
                     } else {
+                        // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+                        if (options->extraFds.size() > 1000) {
+                            return;
+                        }
+
                         std::vector<base::unique_fd> fds = getRandomFds(&provider);
                         CHECK(OK ==
                               p->writeFileDescriptor(fds.begin()->release(),
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index bf877a3..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -120,3 +120,9 @@
     do {                                                                 \
         TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
     } while (0)
+
+// Override the definition of __assert from binder_status.h
+#ifndef __BIONIC__
+#undef __assert
+#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
+#endif // __BIONIC__
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 480ec79..1109ad8 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -36,6 +36,9 @@
 status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
                                 bool /*allowIsolated*/,
                                 int /*dumpsysFlags*/) {
+    if (service == nullptr) {
+        return UNEXPECTED_NULL;
+    }
     mNameToService[name] = service;
     return NO_ERROR;
 }
@@ -103,4 +106,8 @@
     std::vector<IServiceManager::ServiceDebugInfo> ret;
     return ret;
 }
+
+void ServiceManager::clear() {
+    mNameToService.clear();
+}
 }  // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
index ee0637e..ba6bb7d 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
@@ -64,6 +64,9 @@
 
     std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override;
 
+    // Clear all of the registered services
+    void clear();
+
 private:
     std::map<String16, sp<IBinder>> mNameToService;
 };
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 71e5abe..8682c1c 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -50,6 +50,12 @@
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
 }
 
+TEST(AddService, SadNullBinder) {
+    auto sm = new ServiceManager();
+    EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
+}
+
 TEST(AddService, HappyOverExistingService) {
     auto sm = new ServiceManager();
     EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -58,6 +64,15 @@
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
 }
 
+TEST(AddService, HappyClearAddedService) {
+    auto sm = new ServiceManager();
+    EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+    EXPECT_NE(sm->getService(String16("foo")), nullptr);
+    sm->clear();
+    EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
 TEST(GetService, HappyHappy) {
     auto sm = new ServiceManager();
     sp<IBinder> service = getBinder();
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index df0b271..8e57152 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -22,6 +22,7 @@
         "flags_test.cpp",
         "future_test.cpp",
         "match_test.cpp",
+        "mixins_test.cpp",
         "non_null_test.cpp",
         "optional_test.cpp",
         "shared_mutex_test.cpp",
diff --git a/libs/ftl/mixins_test.cpp b/libs/ftl/mixins_test.cpp
new file mode 100644
index 0000000..2c9f9df
--- /dev/null
+++ b/libs/ftl/mixins_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2022 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 <ftl/mixins.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace android::test {
+namespace {
+
+// Keep in sync with example usage in header file.
+
+struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+  using Constructible::Constructible;
+};
+
+static_assert(!std::is_default_constructible_v<Id>);
+
+struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+               ftl::Equatable<Color>,
+               ftl::Orderable<Color> {
+  using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Color() == Color(0u));
+static_assert(ftl::to_underlying(Color(-1)) == 255u);
+static_assert(Color(1u) < Color(2u));
+
+struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+                  ftl::Equatable<Sequence>,
+                  ftl::Orderable<Sequence>,
+                  ftl::Incrementable<Sequence> {
+  using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Sequence() == Sequence(-1));
+
+struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+                 ftl::Equatable<Timeout>,
+                 ftl::Addable<Timeout> {
+  using DefaultConstructible::DefaultConstructible;
+};
+
+using namespace std::chrono_literals;
+static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+
+// Construction.
+constexpr Id kId{1234};
+constexpr Sequence kSequence;
+
+// Underlying value.
+static_assert(ftl::to_underlying(Id(-42)) == -42);
+static_assert(ftl::to_underlying(kSequence) == -1);
+
+// Casting.
+static_assert(static_cast<std::int32_t>(Id(-1)) == -1);
+static_assert(static_cast<std::int8_t>(kSequence) == -1);
+
+static_assert(!std::is_convertible_v<std::int32_t, Id>);
+static_assert(!std::is_convertible_v<Id, std::int32_t>);
+
+// Equality.
+static_assert(kId == Id(1234));
+static_assert(kId != Id(123));
+static_assert(kSequence == Sequence(-1));
+
+// Ordering.
+static_assert(Sequence(1) < Sequence(2));
+static_assert(Sequence(2) > Sequence(1));
+static_assert(Sequence(3) <= Sequence(4));
+static_assert(Sequence(4) >= Sequence(3));
+static_assert(Sequence(5) <= Sequence(5));
+static_assert(Sequence(6) >= Sequence(6));
+
+// Incrementing.
+template <typename Op, typename T, typename... Ts>
+constexpr auto mutable_op(Op op, T lhs, Ts... rhs) {
+  const T result = op(lhs, rhs...);
+  return std::make_pair(lhs, result);
+}
+
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Sequence()) ==
+              std::make_pair(Sequence(0), Sequence(0)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Sequence()) ==
+              std::make_pair(Sequence(0), Sequence(-1)));
+
+// Addition.
+
+// `Addable` implies `Incrementable`.
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Timeout()) ==
+              std::make_pair(Timeout(11s), Timeout(11s)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Timeout()) ==
+              std::make_pair(Timeout(11s), Timeout(10s)));
+
+static_assert(Timeout(5s) + Timeout(6s) == Timeout(11s));
+
+static_assert(mutable_op([](auto& lhs, const auto& rhs) { return lhs += rhs; }, Timeout(7s),
+                         Timeout(8s)) == std::make_pair(Timeout(15s), Timeout(15s)));
+
+// Type safety.
+
+namespace traits {
+
+template <typename, typename = void>
+struct is_incrementable : std::false_type {};
+
+template <typename T>
+struct is_incrementable<T, std::void_t<decltype(++std::declval<T&>())>> : std::true_type {};
+
+template <typename T>
+constexpr bool is_incrementable_v = is_incrementable<T>{};
+
+template <typename, typename, typename, typename = void>
+struct has_binary_op : std::false_type {};
+
+template <typename Op, typename T, typename U>
+struct has_binary_op<Op, T, U, std::void_t<decltype(Op{}(std::declval<T&>(), std::declval<U&>()))>>
+    : std::true_type {};
+
+template <typename T, typename U>
+constexpr bool is_equatable_v =
+    has_binary_op<std::equal_to<void>, T, U>{} && has_binary_op<std::not_equal_to<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_orderable_v =
+    has_binary_op<std::less<void>, T, U>{} && has_binary_op<std::less_equal<void>, T, U>{} &&
+    has_binary_op<std::greater<void>, T, U>{} && has_binary_op<std::greater_equal<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_addable_v = has_binary_op<std::plus<void>, T, U>{};
+
+}  // namespace traits
+
+struct Real : ftl::Constructible<Real, float> {
+  using Constructible::Constructible;
+};
+
+static_assert(traits::is_equatable_v<Id, Id>);
+static_assert(!traits::is_equatable_v<Real, Real>);
+static_assert(!traits::is_equatable_v<Id, Color>);
+static_assert(!traits::is_equatable_v<Sequence, Id>);
+static_assert(!traits::is_equatable_v<Id, std::int32_t>);
+static_assert(!traits::is_equatable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_orderable_v<Color, Color>);
+static_assert(!traits::is_orderable_v<Id, Id>);
+static_assert(!traits::is_orderable_v<Real, Real>);
+static_assert(!traits::is_orderable_v<Color, Sequence>);
+static_assert(!traits::is_orderable_v<Color, std::uint8_t>);
+static_assert(!traits::is_orderable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_incrementable_v<Sequence>);
+static_assert(traits::is_incrementable_v<Timeout>);
+static_assert(!traits::is_incrementable_v<Id>);
+static_assert(!traits::is_incrementable_v<Color>);
+static_assert(!traits::is_incrementable_v<Real>);
+
+static_assert(traits::is_addable_v<Timeout, Timeout>);
+static_assert(!traits::is_addable_v<Id, Id>);
+static_assert(!traits::is_addable_v<Real, Real>);
+static_assert(!traits::is_addable_v<Sequence, Sequence>);
+static_assert(!traits::is_addable_v<Timeout, Sequence>);
+static_assert(!traits::is_addable_v<Color, Timeout>);
+
+}  // namespace
+}  // namespace android::test
diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index f7410c2..6b3b6c4 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -164,4 +164,35 @@
                      }));
 }
 
+// Comparison.
+namespace {
+
+constexpr Optional<int> kOptional1 = 1;
+constexpr Optional<int> kAnotherOptional1 = 1;
+constexpr Optional<int> kOptional2 = 2;
+constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty;
+
+constexpr std::optional<int> kStdOptional1 = 1;
+
+static_assert(kOptional1 == kAnotherOptional1);
+
+static_assert(kOptional1 != kOptional2);
+static_assert(kOptional2 != kOptional1);
+
+static_assert(kOptional1 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional1);
+
+static_assert(kOptionalEmpty == kAnotherOptionalEmpty);
+
+static_assert(kOptional1 == kStdOptional1);
+static_assert(kStdOptional1 == kOptional1);
+
+static_assert(kOptional2 != kStdOptional1);
+static_assert(kStdOptional1 != kOptional2);
+
+static_assert(kOptional2 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional2);
+
+} // namespace
+
 }  // namespace android::test
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..a0e75ff 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,6 +42,7 @@
 
 namespace android {
 
+using gui::CallbackId;
 using gui::DisplayCaptureArgs;
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..23d7d50 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -21,22 +21,11 @@
 #include <optional>
 
 #include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
 #include <private/gui/ParcelUtils.h>
 
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
-    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
-    ON_RELEASE_BUFFER,
-    ON_TRANSACTION_QUEUE_STALLED,
-    LAST = ON_TRANSACTION_QUEUE_STALLED,
-};
-
-} // Anonymous namespace
+namespace android::gui {
 
 status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
     status_t err = output->writeUint64(frameNumber);
@@ -274,60 +263,6 @@
     return listenerStats;
 }
 
-class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
-public:
-    explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
-          : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
-    }
-
-    ~BpTransactionCompletedListener() override;
-
-    void onTransactionCompleted(ListenerStats stats) override {
-        callRemoteAsync<decltype(&ITransactionCompletedListener::
-                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
-                                                                  stats);
-    }
-
-    void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                         uint32_t currentMaxAcquiredBufferCount) override {
-        callRemoteAsync<decltype(&ITransactionCompletedListener::
-                                         onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
-                                                           releaseFence,
-                                                           currentMaxAcquiredBufferCount);
-    }
-
-    void onTransactionQueueStalled(const String8& reason) override {
-        callRemoteAsync<
-                decltype(&ITransactionCompletedListener::
-                                 onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
-                                                             reason);
-    }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
-
-IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
-
-status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
-                                                    Parcel* reply, uint32_t flags) {
-    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::ON_TRANSACTION_COMPLETED:
-            return callLocalAsync(data, reply,
-                                  &ITransactionCompletedListener::onTransactionCompleted);
-        case Tag::ON_RELEASE_BUFFER:
-            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
-        case Tag::ON_TRANSACTION_QUEUE_STALLED:
-            return callLocalAsync(data, reply,
-                                  &ITransactionCompletedListener::onTransactionQueueStalled);
-    }
-}
-
 ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
     std::vector<CallbackId> filteredCallbackIds;
     for (const auto& callbackId : callbackIds) {
@@ -366,4 +301,4 @@
 
 const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
 
-}; // namespace android
+}; // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 95962af..0d1a69b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -51,6 +51,7 @@
 
 namespace android {
 
+using gui::CallbackId;
 using gui::FocusRequest;
 using gui::WindowInfoHandle;
 
@@ -388,6 +389,27 @@
     }
 }
 
+void DisplayState::sanitize(int32_t permissions) {
+    if (what & DisplayState::eLayerStackChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eLayerStackChanged;
+            ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+        }
+    }
+    if (what & DisplayState::eDisplayProjectionChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eDisplayProjectionChanged;
+            ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+        }
+    }
+    if (what & DisplayState::eSurfaceChanged) {
+        if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~DisplayState::eSurfaceChanged;
+            ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+        }
+    }
+}
+
 void layer_state_t::sanitize(int32_t permissions) {
     // TODO: b/109894387
     //
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c4fb1cf..edb18a8 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1108,9 +1108,12 @@
     ATRACE_CALL();
     auto& mapper = GraphicBufferMapper::get();
     mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace));
-    mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
-    mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
-    mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
+    if (mHdrMetadataIsSet & HdrMetadata::SMPTE2086)
+        mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
+    if (mHdrMetadataIsSet & HdrMetadata::CTA861_3)
+        mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
+    if (mHdrMetadataIsSet & HdrMetadata::HDR10PLUS)
+        mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
 }
 
 void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
@@ -2252,6 +2255,7 @@
 int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) {
     ALOGV("Surface::setBuffersSmpte2086Metadata");
     Mutex::Autolock lock(mMutex);
+    mHdrMetadataIsSet |= HdrMetadata::SMPTE2086;
     if (metadata) {
         mHdrMetadata.smpte2086 = *metadata;
         mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
@@ -2264,6 +2268,7 @@
 int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) {
     ALOGV("Surface::setBuffersCta8613Metadata");
     Mutex::Autolock lock(mMutex);
+    mHdrMetadataIsSet |= HdrMetadata::CTA861_3;
     if (metadata) {
         mHdrMetadata.cta8613 = *metadata;
         mHdrMetadata.validTypes |= HdrMetadata::CTA861_3;
@@ -2276,6 +2281,7 @@
 int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
     ALOGV("Surface::setBuffersBlobMetadata");
     Mutex::Autolock lock(mMutex);
+    mHdrMetadataIsSet |= HdrMetadata::HDR10PLUS;
     if (size > 0) {
         mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
         mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1e43700..d741c99 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -314,7 +314,8 @@
     }
 }
 
-void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+binder::Status TransactionCompletedListener::onTransactionCompleted(
+        const ListenerStats& listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
     std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
     {
@@ -454,9 +455,10 @@
             }
         }
     }
+    return binder::Status::ok();
 }
 
-void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
+binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
     std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
     {
         std::scoped_lock<std::mutex> lock(mMutex);
@@ -465,6 +467,7 @@
     for (auto const& it : callbackCopy) {
         it.second(reason.c_str());
     }
+    return binder::Status::ok();
 }
 
 void TransactionCompletedListener::addQueueStallListener(
@@ -478,9 +481,12 @@
     mQueueStallListeners.erase(id);
 }
 
-void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
-                                                   sp<Fence> releaseFence,
-                                                   uint32_t currentMaxAcquiredBufferCount) {
+binder::Status TransactionCompletedListener::onReleaseBuffer(
+        const ReleaseCallbackId& callbackId,
+        const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+        int32_t currentMaxAcquiredBufferCount) {
+    sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
+                                          : Fence::NO_FENCE);
     ReleaseBufferCallback callback;
     {
         std::scoped_lock<std::mutex> lock(mMutex);
@@ -489,13 +495,14 @@
     if (!callback) {
         ALOGE("Could not call release buffer callback, buffer not found %s",
               callbackId.to_string().c_str());
-        return;
+        return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
     }
     std::optional<uint32_t> optionalMaxAcquiredBufferCount =
-            currentMaxAcquiredBufferCount == UINT_MAX
+            static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
             ? std::nullopt
             : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
     callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
+    return binder::Status::ok();
 }
 
 ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -825,7 +832,11 @@
                 ->mReleaseCallbackThread
                 .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
     } else {
-        listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+        std::optional<os::ParcelFileDescriptor> fenceFd;
+        if (fence != Fence::NO_FENCE) {
+            fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
+        }
+        listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
     }
 }
 
@@ -2260,12 +2271,12 @@
     return statusTFromBinderStatus(status);
 }
 
-status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
                                                      ui::StaticDisplayInfo* outInfo) {
     using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
     gui::StaticDisplayInfo ginfo;
     binder::Status status =
-            ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(display, &ginfo);
+            ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo);
     if (status.isOk()) {
         // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
         outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
@@ -2309,52 +2320,74 @@
     return statusTFromBinderStatus(status);
 }
 
-status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
-                                                      ui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+                                                          ui::DynamicDisplayInfo*& outInfo) {
+    // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
+    outInfo->supportedDisplayModes.clear();
+    outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
+    for (const auto& mode : ginfo.supportedDisplayModes) {
+        ui::DisplayMode outMode;
+        outMode.id = mode.id;
+        outMode.resolution.width = mode.resolution.width;
+        outMode.resolution.height = mode.resolution.height;
+        outMode.xDpi = mode.xDpi;
+        outMode.yDpi = mode.yDpi;
+        outMode.refreshRate = mode.refreshRate;
+        outMode.appVsyncOffset = mode.appVsyncOffset;
+        outMode.sfVsyncOffset = mode.sfVsyncOffset;
+        outMode.presentationDeadline = mode.presentationDeadline;
+        outMode.group = mode.group;
+        std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+                       std::back_inserter(outMode.supportedHdrTypes),
+                       [](const int32_t& value) { return static_cast<ui::Hdr>(value); });
+        outInfo->supportedDisplayModes.push_back(outMode);
+    }
+
+    outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
+    outInfo->renderFrameRate = ginfo.renderFrameRate;
+
+    outInfo->supportedColorModes.clear();
+    outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
+    for (const auto& cmode : ginfo.supportedColorModes) {
+        outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
+    }
+
+    outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
+
+    std::vector<ui::Hdr> types;
+    types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
+    for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
+        types.push_back(static_cast<ui::Hdr>(hdr));
+    }
+    outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
+                                               ginfo.hdrCapabilities.maxAverageLuminance,
+                                               ginfo.hdrCapabilities.minLuminance);
+
+    outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
+    outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
+    outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+}
+
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
+                                                            ui::DynamicDisplayInfo* outInfo) {
     gui::DynamicDisplayInfo ginfo;
     binder::Status status =
-            ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfo(display, &ginfo);
+            ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId,
+                                                                                   &ginfo);
     if (status.isOk()) {
-        // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
-        outInfo->supportedDisplayModes.clear();
-        outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
-        for (const auto& mode : ginfo.supportedDisplayModes) {
-            ui::DisplayMode outMode;
-            outMode.id = mode.id;
-            outMode.resolution.width = mode.resolution.width;
-            outMode.resolution.height = mode.resolution.height;
-            outMode.xDpi = mode.xDpi;
-            outMode.yDpi = mode.yDpi;
-            outMode.refreshRate = mode.refreshRate;
-            outMode.appVsyncOffset = mode.appVsyncOffset;
-            outMode.sfVsyncOffset = mode.sfVsyncOffset;
-            outMode.presentationDeadline = mode.presentationDeadline;
-            outMode.group = mode.group;
-            outInfo->supportedDisplayModes.push_back(outMode);
-        }
+        getDynamicDisplayInfoInternal(ginfo, outInfo);
+    }
+    return statusTFromBinderStatus(status);
+}
 
-        outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
-
-        outInfo->supportedColorModes.clear();
-        outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
-        for (const auto& cmode : ginfo.supportedColorModes) {
-            outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
-        }
-
-        outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
-
-        std::vector<ui::Hdr> types;
-        types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
-        for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
-            types.push_back(static_cast<ui::Hdr>(hdr));
-        }
-        outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
-                                                   ginfo.hdrCapabilities.maxAverageLuminance,
-                                                   ginfo.hdrCapabilities.minLuminance);
-
-        outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
-        outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
-        outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+                                                               ui::DynamicDisplayInfo* outInfo) {
+    gui::DynamicDisplayInfo ginfo;
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display,
+                                                                                      &ginfo);
+    if (status.isOk()) {
+        getDynamicDisplayInfoInternal(ginfo, outInfo);
     }
     return statusTFromBinderStatus(status);
 }
@@ -2362,7 +2395,8 @@
 status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
                                                      ui::DisplayMode* mode) {
     ui::DynamicDisplayInfo info;
-    status_t result = getDynamicDisplayInfo(display, &info);
+
+    status_t result = getDynamicDisplayInfoFromToken(display, &info);
     if (result != NO_ERROR) {
         return result;
     }
@@ -2538,7 +2572,7 @@
     gui::PullAtomData pad;
     binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad);
     if (status.isOk()) {
-        outData->assign((const char*)pad.data.data(), pad.data.size());
+        outData->assign(pad.data.begin(), pad.data.end());
         *success = pad.success;
     }
     return statusTFromBinderStatus(status);
@@ -2823,7 +2857,11 @@
 
         while (!callbackInfos.empty()) {
             auto [callbackId, releaseFence] = callbackInfos.front();
-            listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+            std::optional<os::ParcelFileDescriptor> fenceFd;
+            if (releaseFence != Fence::NO_FENCE) {
+                fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+            }
+            listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
             callbackInfos.pop();
         }
 
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index 3cd77f8..ce30426 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -27,6 +27,7 @@
     Size resolution;
     float xDpi = 0.0f;
     float yDpi = 0.0f;
+    int[] supportedHdrTypes;
 
     float refreshRate = 0.0f;
     long appVsyncOffset = 0;
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 57e6081..3114929 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -27,6 +27,7 @@
     List<DisplayMode> supportedDisplayModes;
 
     int activeDisplayModeId;
+    float renderFrameRate;
 
     int[] supportedColorModes;
     int activeColorMode;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 40410fb..488a148 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -126,12 +126,14 @@
     /**
      * Gets immutable information about given physical display.
      */
-    StaticDisplayInfo getStaticDisplayInfo(IBinder display);
+    StaticDisplayInfo getStaticDisplayInfo(long displayId);
 
     /**
      * Gets dynamic information about given physical display.
      */
-    DynamicDisplayInfo getDynamicDisplayInfo(IBinder display);
+    DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId);
+
+    DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display);
 
     DisplayPrimaries getDisplayNativePrimaries(IBinder display);
 
diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
new file mode 100644
index 0000000..dde4d38
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.ListenerStats;
+import android.gui.ReleaseCallbackId;
+
+/** @hide */
+oneway interface ITransactionCompletedListener {
+   void onTransactionCompleted(in ListenerStats stats);
+
+   void onReleaseBuffer(in ReleaseCallbackId callbackId,
+                        in @nullable ParcelFileDescriptor releaseFenceFd,
+                        int currentMaxAcquiredBufferCount);
+
+   void onTransactionQueueStalled(@utf8InCpp String name);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
similarity index 80%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ListenerStats.aidl
index d62891b..63248b2 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ListenerStats.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2022 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-parcelable SingleDataParcelable{
-   int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl
index 14d33c6..c307cef 100644
--- a/libs/gui/aidl/android/gui/PullAtomData.aidl
+++ b/libs/gui/aidl/android/gui/PullAtomData.aidl
@@ -18,6 +18,6 @@
 
 /** @hide */
 parcelable PullAtomData {
-    @utf8InCpp String data;
+    byte[] data;
     boolean success;
 }
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
similarity index 79%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
index d62891b..c86de34 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2022 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-parcelable SingleDataParcelable{
-   int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 1c61d6b..82e1b5a 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -62,7 +62,6 @@
         "libutils",
         "libnativewindow",
         "libvndksupport",
-        "libbufferhubqueue",
     ],
     header_libs: [
         "libdvr_headers",
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 9d1ee8f..8810e4e 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -79,9 +79,11 @@
                 (override));
     MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
                 (override));
-    MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+    MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
                 (override));
-    MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+    MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
+                (override));
+    MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
                 (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
     MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
                 (const sp<IBinder>&, gui::DisplayPrimaries*), (override));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d517e99..d70a7f0 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,11 +23,11 @@
 #include <android/gui/IHdrLayerInfoListener.h>
 #include <android/gui/IRegionSamplingListener.h>
 #include <android/gui/IScreenCaptureListener.h>
+#include <android/gui/ITransactionCompletedListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
 #include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <gui/SpHash.h>
 #include <math/vec4.h>
 #include <stdint.h>
@@ -66,6 +66,7 @@
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
 using gui::IScreenCaptureListener;
+using gui::ListenerCallbacks;
 using gui::SpHash;
 
 namespace gui {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6ec6bd7..c5fdf82 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/gui/ITransactionCompletedListener.h>
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <android/native_window.h>
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
 #include <android/gui/DropInputMode.h>
@@ -35,6 +35,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerCaptureArgs.h>
 #include <gui/LayerMetadata.h>
+#include <gui/ReleaseCallbackId.h>
 #include <gui/SpHash.h>
 #include <gui/SurfaceControl.h>
 #include <gui/WindowInfo.h>
@@ -56,6 +57,9 @@
 using gui::ISurfaceComposerClient;
 using gui::LayerMetadata;
 
+using gui::ITransactionCompletedListener;
+using gui::ReleaseCallbackId;
+
 struct client_cache_t {
     wp<IBinder> token = nullptr;
     uint64_t id;
@@ -359,6 +363,7 @@
 
     DisplayState();
     void merge(const DisplayState& other);
+    void sanitize(int32_t permissions);
 
     uint32_t what = 0;
     uint32_t flags = 0;
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h
similarity index 81%
rename from libs/gui/include/gui/ITransactionCompletedListener.h
rename to libs/gui/include/gui/ListenerStats.h
index 453e8f3..3a12802 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ListenerStats.h
@@ -24,6 +24,8 @@
 #include <binder/SafeInterface.h>
 
 #include <gui/FrameTimestamps.h>
+#include <gui/ReleaseCallbackId.h>
+
 #include <ui/Fence.h>
 #include <utils/Timers.h>
 
@@ -32,10 +34,7 @@
 #include <unordered_set>
 #include <variant>
 
-namespace android {
-
-class ITransactionCompletedListener;
-class ListenerCallbacks;
+namespace android::gui {
 
 class CallbackId : public Parcelable {
 public:
@@ -54,30 +53,6 @@
     std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
 };
 
-class ReleaseCallbackId : public Parcelable {
-public:
-    static const ReleaseCallbackId INVALID_ID;
-
-    uint64_t bufferId;
-    uint64_t framenumber;
-    ReleaseCallbackId() {}
-    ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
-          : bufferId(bufferId), framenumber(framenumber) {}
-    status_t writeToParcel(Parcel* output) const override;
-    status_t readFromParcel(const Parcel* input) override;
-
-    bool operator==(const ReleaseCallbackId& rhs) const {
-        return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
-    }
-    bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
-    std::string to_string() const {
-        if (*this == INVALID_ID) return "INVALID_ID";
-
-        return "bufferId:" + std::to_string(bufferId) +
-                " framenumber:" + std::to_string(framenumber);
-    }
-};
-
 struct ReleaseBufferCallbackIdHash {
     std::size_t operator()(const ReleaseCallbackId& key) const {
         return std::hash<uint64_t>()(key.bufferId);
@@ -186,27 +161,6 @@
     std::vector<TransactionStats> transactionStats;
 };
 
-class ITransactionCompletedListener : public IInterface {
-public:
-    DECLARE_META_INTERFACE(TransactionCompletedListener)
-
-    virtual void onTransactionCompleted(ListenerStats stats) = 0;
-
-    virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                                 uint32_t currentMaxAcquiredBufferCount) = 0;
-
-    virtual void onTransactionQueueStalled(const String8& name) = 0;
-};
-
-class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
-public:
-    BnTransactionCompletedListener()
-          : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-
 class ListenerCallbacks {
 public:
     ListenerCallbacks(const sp<IBinder>& listener,
@@ -268,4 +222,4 @@
     }
 };
 
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
new file mode 100644
index 0000000..142ee5a
--- /dev/null
+++ b/libs/gui/include/gui/ReleaseCallbackId.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <cstdint>
+
+namespace android::gui {
+
+class ReleaseCallbackId : public Parcelable {
+public:
+    static const ReleaseCallbackId INVALID_ID;
+
+    uint64_t bufferId;
+    uint64_t framenumber;
+    ReleaseCallbackId() {}
+    ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+          : bufferId(bufferId), framenumber(framenumber) {}
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    bool operator==(const ReleaseCallbackId& rhs) const {
+        return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+    }
+    bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+    std::string to_string() const {
+        if (*this == INVALID_ID) return "INVALID_ID";
+
+        return "bufferId:" + std::to_string(bufferId) +
+                " framenumber:" + std::to_string(framenumber);
+    }
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 7aec0bf..b9ccdc9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -486,6 +486,11 @@
     // queue operation.  There is no HDR metadata by default.
     HdrMetadata mHdrMetadata;
 
+    // mHdrMetadataIsSet is a bitfield to track which HDR metadata has been set.
+    // Prevent Surface from resetting HDR metadata that was set on a bufer when
+    // HDR metadata is not set on this Surface.
+    uint32_t mHdrMetadataIsSet{0};
+
     // mCrop is the crop rectangle that will be used for the next buffer
     // that gets queued. It is set by calling setCrop.
     Rect mCrop;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2038f14..96d3a23 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,10 +42,13 @@
 
 #include <android/gui/ISurfaceComposerClient.h>
 
+#include <android/gui/BnTransactionCompletedListener.h>
+
 #include <gui/CpuConsumer.h>
 #include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
 #include <gui/SurfaceControl.h>
 #include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
@@ -59,11 +62,21 @@
 class ITunnelModeEnabledListener;
 class Region;
 
+using gui::BnTransactionCompletedListener;
+using gui::CallbackId;
+using gui::CallbackIdHash;
 using gui::DisplayCaptureArgs;
+using gui::FrameEventHistoryStats;
 using gui::IRegionSamplingListener;
 using gui::ISurfaceComposerClient;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
 using gui::LayerCaptureArgs;
 using gui::LayerMetadata;
+using gui::ListenerStats;
+using gui::ReleaseBufferCallbackIdHash;
+using gui::ReleaseCallbackId;
+using gui::SurfaceStats;
 
 struct SurfaceControlStats {
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -149,10 +162,10 @@
     static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
 
     // Get immutable information about given physical display.
-    static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
+    static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*);
 
-    // Get dynamic information about given physical display.
-    static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
+    // Get dynamic information about given physical display from display id
+    static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*);
 
     // Shorthand for the active display mode from getDynamicDisplayInfo().
     // TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
@@ -714,6 +727,12 @@
     ReleaseCallbackThread mReleaseCallbackThread;
 
 private:
+    // Get dynamic information about given physical display from token
+    static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+                                                   ui::DynamicDisplayInfo*);
+
+    static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+                                              ui::DynamicDisplayInfo*& outInfo);
     virtual void onFirstRef();
 
     mutable     Mutex                       mLock;
@@ -819,17 +838,17 @@
     void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
 
     // BnTransactionCompletedListener overrides
-    void onTransactionCompleted(ListenerStats stats) override;
-    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
-                         uint32_t currentMaxAcquiredBufferCount) override;
+    binder::Status onTransactionCompleted(const ListenerStats& stats) override;
+    binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
+                                   const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+                                   int32_t currentMaxAcquiredBufferCount) override;
+    binder::Status onTransactionQueueStalled(const std::string& reason) override;
 
     void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
 
     // For Testing Only
     static void setInstance(const sp<TransactionCompletedListener>&);
 
-    void onTransactionQueueStalled(const String8& reason) override;
-
 private:
     ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
     static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6d3b425..55242df 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -782,13 +782,18 @@
         return binder::Status::ok();
     }
 
-    binder::Status getStaticDisplayInfo(const sp<IBinder>& /*display*/,
+    binder::Status getStaticDisplayInfo(int64_t /*displayId*/,
                                         gui::StaticDisplayInfo* /*outInfo*/) override {
         return binder::Status::ok();
     }
 
-    binder::Status getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
-                                         gui::DynamicDisplayInfo* /*outInfo*/) override {
+    binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/,
+                                               gui::DynamicDisplayInfo* /*outInfo*/) override {
+        return binder::Status::ok();
+    }
+
+    binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/,
+                                                  gui::DynamicDisplayInfo* /*outInfo*/) override {
         return binder::Status::ok();
     }
 
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3685f54..9e8ebf3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -21,6 +21,7 @@
 #include <cutils/compiler.h>
 #include <inttypes.h>
 #include <string.h>
+#include <optional>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -552,21 +553,21 @@
                                 &pointerCoords[getPointerCount()]);
 }
 
-int MotionEvent::getSurfaceRotation() const {
+std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const {
     // The surface rotation is the rotation from the window's coordinate space to that of the
     // display. Since the event's transform takes display space coordinates to window space, the
     // returned surface rotation is the inverse of the rotation for the surface.
     switch (mTransform.getOrientation()) {
         case ui::Transform::ROT_0:
-            return DISPLAY_ORIENTATION_0;
+            return ui::ROTATION_0;
         case ui::Transform::ROT_90:
-            return DISPLAY_ORIENTATION_270;
+            return ui::ROTATION_270;
         case ui::Transform::ROT_180:
-            return DISPLAY_ORIENTATION_180;
+            return ui::ROTATION_180;
         case ui::Transform::ROT_270:
-            return DISPLAY_ORIENTATION_90;
+            return ui::ROTATION_90;
         default:
-            return -1;
+            return std::nullopt;
     }
 }
 
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 422e6e0..fa5c41f 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -43,7 +43,6 @@
 // Enables debug output for mapping.
 #define DEBUG_MAPPING 0
 
-
 namespace android {
 
 static const char* WHITESPACE = " \t\r";
@@ -93,6 +92,7 @@
       : mType(other.mType),
         mLoadFileName(other.mLoadFileName),
         mLayoutOverlayApplied(other.mLayoutOverlayApplied),
+        mKeyRemapping(other.mKeyRemapping),
         mKeysByScanCode(other.mKeysByScanCode),
         mKeysByUsageCode(other.mKeysByUsageCode) {
     for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -114,7 +114,7 @@
     if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
         return false;
     }
-    if (mKeys.size() != other.mKeys.size() ||
+    if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
         mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
         mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
         return false;
@@ -131,22 +131,9 @@
         }
     }
 
-    for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
-        if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
-            return false;
-        }
-        if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
-            return false;
-        }
-    }
-
-    for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
-        if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
-            return false;
-        }
-        if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
-            return false;
-        }
+    if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
+        mKeysByUsageCode != other.mKeysByUsageCode) {
+        return false;
     }
 
     return true;
@@ -258,14 +245,12 @@
         }
     }
 
-    for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
-        mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
-                                        overlay.mKeysByScanCode.valueAt(i));
+    for (auto const& it : overlay.mKeysByScanCode) {
+        mKeysByScanCode.insert_or_assign(it.first, it.second);
     }
 
-    for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
-        mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
-                                         overlay.mKeysByUsageCode.valueAt(i));
+    for (auto const& it : overlay.mKeysByUsageCode) {
+        mKeysByUsageCode.insert_or_assign(it.first, it.second);
     }
     mLayoutOverlayApplied = true;
 }
@@ -400,11 +385,26 @@
     return true;
 }
 
+void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+    if (fromKeyCode == toKeyCode) {
+        mKeyRemapping.erase(fromKeyCode);
+#if DEBUG_MAPPING
+        ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
+#endif
+        return;
+    }
+    mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+#if DEBUG_MAPPING
+    ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
+          toKeyCode);
+#endif
+}
+
 status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
     if (usageCode) {
-        ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
-        if (index >= 0) {
-            *outKeyCode = mKeysByUsageCode.valueAt(index);
+        const auto it = mKeysByUsageCode.find(usageCode);
+        if (it != mKeysByUsageCode.end()) {
+            *outKeyCode = it->second;
 #if DEBUG_MAPPING
             ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
                     scanCode, usageCode, *outKeyCode);
@@ -413,9 +413,9 @@
         }
     }
     if (scanCode) {
-        ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
-        if (index >= 0) {
-            *outKeyCode = mKeysByScanCode.valueAt(index);
+        const auto it = mKeysByScanCode.find(scanCode);
+        if (it != mKeysByScanCode.end()) {
+            *outKeyCode = it->second;
 #if DEBUG_MAPPING
             ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
                     scanCode, usageCode, *outKeyCode);
@@ -431,45 +431,59 @@
     return NAME_NOT_FOUND;
 }
 
-void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState,
-                                  int32_t *outKeyCode, int32_t *outMetaState) const {
-    *outKeyCode = keyCode;
-    *outMetaState = metaState;
+int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
+    int32_t toKeyCode = fromKeyCode;
 
-    const Behavior* behavior = getKeyBehavior(keyCode, metaState);
+    const auto it = mKeyRemapping.find(fromKeyCode);
+    if (it != mKeyRemapping.end()) {
+        toKeyCode = it->second;
+    }
+#if DEBUG_MAPPING
+    ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
+#endif
+    return toKeyCode;
+}
+
+std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
+                                                              int32_t fromMetaState) const {
+    int32_t toKeyCode = fromKeyCode;
+    int32_t toMetaState = fromMetaState;
+
+    const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
     if (behavior != nullptr) {
         if (behavior->replacementKeyCode) {
-            *outKeyCode = behavior->replacementKeyCode;
-            int32_t newMetaState = metaState & ~behavior->metaState;
+            toKeyCode = behavior->replacementKeyCode;
+            toMetaState = fromMetaState & ~behavior->metaState;
             // Reset dependent meta states.
             if (behavior->metaState & AMETA_ALT_ON) {
-                newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
+                toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
             }
             if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
-                newMetaState &= ~AMETA_ALT_ON;
+                toMetaState &= ~AMETA_ALT_ON;
             }
             if (behavior->metaState & AMETA_CTRL_ON) {
-                newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
+                toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
             }
             if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
-                newMetaState &= ~AMETA_CTRL_ON;
+                toMetaState &= ~AMETA_CTRL_ON;
             }
             if (behavior->metaState & AMETA_SHIFT_ON) {
-                newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
+                toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
             }
             if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
-                newMetaState &= ~AMETA_SHIFT_ON;
+                toMetaState &= ~AMETA_SHIFT_ON;
             }
             // ... and put universal bits back if needed
-            *outMetaState = normalizeMetaState(newMetaState);
+            toMetaState = normalizeMetaState(toMetaState);
         }
     }
 
 #if DEBUG_MAPPING
-    ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ "
-            "replacement keyCode=%d, replacement metaState=0x%08x.",
-            keyCode, metaState, *outKeyCode, *outMetaState);
+    ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
+          "replacement keyCode=%d, replacement metaState=0x%08x.",
+          fromKeyCode, fromMetaState, toKeyCode, toMetaState);
 #endif
+    return std::make_pair(toKeyCode, toMetaState);
 }
 
 bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
@@ -720,6 +734,18 @@
             return nullptr;
         }
     }
+    size_t numKeyRemapping = parcel->readInt32();
+    if (parcel->errorCheck()) {
+        return nullptr;
+    }
+    for (size_t i = 0; i < numKeyRemapping; i++) {
+        int32_t key = parcel->readInt32();
+        int32_t value = parcel->readInt32();
+        map->mKeyRemapping.insert_or_assign(key, value);
+        if (parcel->errorCheck()) {
+            return nullptr;
+        }
+    }
     size_t numKeysByScanCode = parcel->readInt32();
     if (parcel->errorCheck()) {
         return nullptr;
@@ -727,7 +753,7 @@
     for (size_t i = 0; i < numKeysByScanCode; i++) {
         int32_t key = parcel->readInt32();
         int32_t value = parcel->readInt32();
-        map->mKeysByScanCode.add(key, value);
+        map->mKeysByScanCode.insert_or_assign(key, value);
         if (parcel->errorCheck()) {
             return nullptr;
         }
@@ -739,7 +765,7 @@
     for (size_t i = 0; i < numKeysByUsageCode; i++) {
         int32_t key = parcel->readInt32();
         int32_t value = parcel->readInt32();
-        map->mKeysByUsageCode.add(key, value);
+        map->mKeysByUsageCode.insert_or_assign(key, value);
         if (parcel->errorCheck()) {
             return nullptr;
         }
@@ -773,17 +799,23 @@
         }
         parcel->writeInt32(0);
     }
+    size_t numKeyRemapping = mKeyRemapping.size();
+    parcel->writeInt32(numKeyRemapping);
+    for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
+        parcel->writeInt32(fromAndroidKeyCode);
+        parcel->writeInt32(toAndroidKeyCode);
+    }
     size_t numKeysByScanCode = mKeysByScanCode.size();
     parcel->writeInt32(numKeysByScanCode);
-    for (size_t i = 0; i < numKeysByScanCode; i++) {
-        parcel->writeInt32(mKeysByScanCode.keyAt(i));
-        parcel->writeInt32(mKeysByScanCode.valueAt(i));
+    for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
+        parcel->writeInt32(fromScanCode);
+        parcel->writeInt32(toAndroidKeyCode);
     }
     size_t numKeysByUsageCode = mKeysByUsageCode.size();
     parcel->writeInt32(numKeysByUsageCode);
-    for (size_t i = 0; i < numKeysByUsageCode; i++) {
-        parcel->writeInt32(mKeysByUsageCode.keyAt(i));
-        parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+    for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
+        parcel->writeInt32(fromUsageCode);
+        parcel->writeInt32(toAndroidKeyCode);
     }
 }
 #endif // __linux__
@@ -950,9 +982,9 @@
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
     }
-    KeyedVector<int32_t, int32_t>& map =
-            mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
-    if (map.indexOfKey(code) >= 0) {
+    std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+    const auto it = map.find(code);
+    if (it != map.end()) {
         ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
@@ -971,7 +1003,7 @@
     ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
             mapUsage ? "usage" : "scan code", code, keyCode);
 #endif
-    map.add(code, keyCode);
+    map.insert_or_assign(code, keyCode);
     return NO_ERROR;
 }
 
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 16ffa10..ed9ac9f 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -116,25 +116,24 @@
 
     Tokenizer* rawTokenizer;
     status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
-    std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
     if (status) {
-        ALOGE("Error %d opening property file %s.", status, filename);
-    } else {
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
-            Parser parser(outMap.get(), tokenizer.get());
-            status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
-            ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
-                  tokenizer->getFilename().string(), tokenizer->getLineNumber(),
-                  elapsedTime / 1000000.0);
-#endif
-            if (status) {
-                return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
-            }
+        return android::base::Error(-status) << "Could not open file: " << filename;
     }
+#if DEBUG_PARSER_PERFORMANCE
+    nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+    std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
+    Parser parser(outMap.get(), tokenizer.get());
+    status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+    nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+    ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(),
+          tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+    if (status) {
+        return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
+    }
+
     return std::move(outMap);
 }
 
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c62e098..c9393f4 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -40,17 +40,20 @@
 
 const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
 
-void TouchVideoFrame::rotate(int32_t orientation) {
+void TouchVideoFrame::rotate(ui::Rotation orientation) {
     switch (orientation) {
-        case DISPLAY_ORIENTATION_90:
+        case ui::ROTATION_90:
             rotateQuarterTurn(false /*clockwise*/);
             break;
-        case DISPLAY_ORIENTATION_180:
+        case ui::ROTATION_180:
             rotate180();
             break;
-        case DISPLAY_ORIENTATION_270:
+        case ui::ROTATION_270:
             rotateQuarterTurn(true /*clockwise*/);
             break;
+        case ui::ROTATION_0:
+            // No need to rotate if there's no rotation.
+            break;
     }
 }
 
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 654b236..081a995 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -73,38 +73,38 @@
 TEST(TouchVideoFrame, Rotate90_0x0) {
     TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
     TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_90);
+    frame.rotate(ui::ROTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate90_1x1) {
     TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
     TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_90);
+    frame.rotate(ui::ROTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate90_2x2) {
     TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
     TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_90);
+    frame.rotate(ui::ROTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate90_3x2) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_90);
+    frame.rotate(ui::ROTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate90_3x2_4times) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_90);
-    frame.rotate(DISPLAY_ORIENTATION_90);
-    frame.rotate(DISPLAY_ORIENTATION_90);
-    frame.rotate(DISPLAY_ORIENTATION_90);
+    frame.rotate(ui::ROTATION_90);
+    frame.rotate(ui::ROTATION_90);
+    frame.rotate(ui::ROTATION_90);
+    frame.rotate(ui::ROTATION_90);
     ASSERT_EQ(frame, frameOriginal);
 }
 
@@ -113,43 +113,43 @@
 TEST(TouchVideoFrame, Rotate180_0x0) {
     TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
     TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate180_1x1) {
     TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
     TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate180_2x2) {
     TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
     TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate180_3x2) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate180_3x2_2times) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameOriginal);
 }
 
 TEST(TouchVideoFrame, Rotate180_3x3) {
     TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
     TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_180);
+    frame.rotate(ui::ROTATION_180);
     ASSERT_EQ(frame, frameRotated);
 }
 
@@ -158,38 +158,38 @@
 TEST(TouchVideoFrame, Rotate270_0x0) {
     TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
     TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_270);
+    frame.rotate(ui::ROTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate270_1x1) {
     TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
     TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_270);
+    frame.rotate(ui::ROTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate270_2x2) {
     TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
     TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_270);
+    frame.rotate(ui::ROTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate270_3x2) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_270);
+    frame.rotate(ui::ROTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate270_3x2_4times) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
     TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
-    frame.rotate(DISPLAY_ORIENTATION_270);
-    frame.rotate(DISPLAY_ORIENTATION_270);
-    frame.rotate(DISPLAY_ORIENTATION_270);
-    frame.rotate(DISPLAY_ORIENTATION_270);
+    frame.rotate(ui::ROTATION_270);
+    frame.rotate(ui::ROTATION_270);
+    frame.rotate(ui::ROTATION_270);
+    frame.rotate(ui::ROTATION_270);
     ASSERT_EQ(frame, frameOriginal);
 }
 
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 0375915..54af7c9 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -31,6 +31,7 @@
     srcs: [
         "recoverymap.cpp",
         "recoverymapmath.cpp",
+        "recoverymaputils.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index df24b10..5c9c8b6 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -61,6 +61,21 @@
      * decompressImage().
      */
     size_t getDecompressedImageHeight();
+    /*
+     * Returns the XMP data from the image.
+     */
+    void* getXMPPtr();
+    /*
+     * Returns the decompressed XMP buffer size. This method must be called only after
+     * calling decompressImage().
+     */
+    size_t getXMPSize();
+
+    bool getCompressedImageParameters(const void* image, int length,
+                              size_t* pWidth, size_t* pHeight,
+                              std::vector<uint8_t>* &iccData,
+                              std::vector<uint8_t>* &exifData);
+
 private:
     bool decode(const void* image, int length);
     // Returns false if errors occur.
@@ -72,6 +87,9 @@
     static const int kCompressBatchSize = 16;
     // The buffer that holds the decompressed result.
     std::vector<JOCTET> mResultBuffer;
+    // The buffer that holds XMP Data.
+    std::vector<JOCTET> mXMPBuffer;
+
     // Resolution of the decompressed image.
     size_t mWidth;
     size_t mHeight;
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 9f53a57..6995762 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -44,6 +44,7 @@
     ERROR_JPEGR_DECODE_ERROR            = JPEGR_RUNTIME_ERROR_BASE - 2,
     ERROR_JPEGR_CALCULATION_ERROR       = JPEGR_RUNTIME_ERROR_BASE - 3,
     ERROR_JPEGR_METADATA_ERROR          = JPEGR_RUNTIME_ERROR_BASE - 4,
+    ERROR_JPEGR_TONEMAP_ERROR           = JPEGR_RUNTIME_ERROR_BASE - 5,
 };
 
 }  // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 10a9db8..74f9776 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -34,6 +34,13 @@
   JPEGR_TF_PQ = 1,
 } jpegr_transfer_function;
 
+struct jpegr_info_struct {
+    size_t width;
+    size_t height;
+    std::vector<uint8_t>* iccData;
+    std::vector<uint8_t>* exifData;
+};
+
 /*
  * Holds information for uncompressed image or recovery map.
  */
@@ -117,10 +124,33 @@
 typedef struct jpegr_compressed_struct* jr_compressed_ptr;
 typedef struct jpegr_exif_struct* jr_exif_ptr;
 typedef struct jpegr_metadata* jr_metadata_ptr;
+typedef struct jpegr_info_struct* jr_info_ptr;
 
 class RecoveryMap {
 public:
     /*
+     * Encode API-0
+     * Compress JPEGR image from 10-bit HDR YUV.
+     *
+     * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images,
+     * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed
+     * JPEG.
+     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+     * @param hdr_tf transfer function of the HDR image
+     * @param dest destination of the compressed JPEGR image
+     * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+     *                the highest quality
+     * @param exif pointer to the exif metadata.
+     * @return NO_ERROR if encoding succeeds, error code if error occurs.
+     */
+    status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+                         jpegr_transfer_function hdr_tf,
+                         jr_compressed_ptr dest,
+                         int quality,
+                         jr_exif_ptr exif);
+
+    /*
+     * Encode API-1
      * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
      *
      * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
@@ -143,6 +173,7 @@
                          jr_exif_ptr exif);
 
     /*
+     * Encode API-2
      * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
      *
      * This method requires HAL Hardware JPEG encoder.
@@ -151,6 +182,8 @@
      * compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
      * @param uncompressed_p010_image uncompressed HDR image in P010 color format
      * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+     *                                   Note: the SDR image must be the decoded version of the JPEG
+     *                                         input
      * @param compressed_jpeg_image compressed 8-bit JPEG image
      * @param hdr_tf transfer function of the HDR image
      * @param dest destination of the compressed JPEGR image
@@ -163,6 +196,7 @@
                          jr_compressed_ptr dest);
 
     /*
+     * Encode API-3
      * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
      *
      * This method requires HAL Hardware JPEG encoder.
@@ -182,6 +216,7 @@
                          jr_compressed_ptr dest);
 
     /*
+     * Decode API
      * Decompress JPEGR image.
      *
      * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
@@ -200,8 +235,19 @@
      */
     status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                          jr_uncompressed_ptr dest,
-                         jr_exif_ptr exif,
-                         bool request_sdr);
+                         jr_exif_ptr exif = nullptr,
+                         bool request_sdr = false);
+
+    /*
+    * Gets Info from JPEGR file without decoding it.
+    *
+    * The output is filled jpegr_info structure
+    * @param compressed_jpegr_image compressed JPEGR image
+    * @param jpegr_info pointer to output JPEGR info
+    * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
+    */
+    status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+                          jr_info_ptr jpegr_info);
 private:
     /*
      * This method is called in the decoding pipeline. It will decode the recovery map.
@@ -260,17 +306,26 @@
                               jr_uncompressed_ptr dest);
 
     /*
+     * This methoud is called to separate primary image and recovery map image from JPEGR
+     *
+     * @param compressed_jpegr_image compressed JPEGR image
+     * @param primary_image destination of primary image
+     * @param recovery_map destination of compressed recovery map
+     * @return NO_ERROR if calculation succeeds, error code if error occurs.
+    */
+    status_t extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+                                               jr_compressed_ptr primary_image,
+                                               jr_compressed_ptr recovery_map);
+    /*
      * This method is called in the decoding pipeline. It will read XMP metadata to find the start
      * position of the compressed recovery map, and will extract the compressed recovery map.
      *
      * @param compressed_jpegr_image compressed JPEGR image
      * @param dest destination of compressed recovery map
-     * @param metadata destination of the recovery map metadata
      * @return NO_ERROR if calculation succeeds, error code if error occurs.
      */
     status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
-                                jr_compressed_ptr dest,
-                                jr_metadata_ptr metadata);
+                                jr_compressed_ptr dest);
 
     /*
      * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
@@ -328,6 +383,16 @@
      * @return XMP metadata in type of string
      */
     std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+
+    /*
+     * This method will tone map a HDR image to an SDR image.
+     *
+     * @param uncompressed_p010_image (input) uncompressed P010 image
+     * @param dest (output) tone mapping result as a YUV_420 image
+     * @return NO_ERROR if calculation succeeds, error code if error occurs.
+     */
+    status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+                     jr_uncompressed_ptr dest);
 };
 
 } // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index fe7a651..0fb64d3 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -27,6 +27,8 @@
 // Framework
 
 const float kSdrWhiteNits = 100.0f;
+const float kHlgMaxNits = 1000.0f;
+const float kPqMaxNits = 10000.0f;
 
 struct Color {
   union {
@@ -113,9 +115,14 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // sRGB transformations
+// NOTE: sRGB has the same color primaries as BT.709, but different transfer
+// function. For this reason, all sRGB transformations here apply to BT.709,
+// except for those concerning transfer functions.
 
 /*
  * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1.
+ *
+ * [0.0, 1.0] range in and out.
  */
 float srgbLuminance(Color e);
 
@@ -142,7 +149,9 @@
 // Display-P3 transformations
 
 /*
- * Calculated the luminance of a linear RGB P3 pixel, according to EG 432-1.
+ * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1.
+ *
+ * [0.0, 1.0] range in and out.
  */
 float p3Luminance(Color e);
 
@@ -152,6 +161,8 @@
 
 /*
  * Calculate the luminance of a linear RGB BT.2100 pixel.
+ *
+ * [0.0, 1.0] range in and out.
  */
 float bt2100Luminance(Color e);
 
@@ -166,23 +177,35 @@
 Color bt2100YuvToRgb(Color e_gamma);
 
 /*
- * Convert from scene luminance in nits to HLG.
+ * Convert from scene luminance to HLG.
+ *
+ * [0.0, 1.0] range in and out.
  */
+float hlgOetf(float e);
 Color hlgOetf(Color e);
 
 /*
- * Convert from HLG to scene luminance in nits.
+ * Convert from HLG to scene luminance.
+ *
+ * [0.0, 1.0] range in and out.
  */
+float hlgInvOetf(float e_gamma);
 Color hlgInvOetf(Color e_gamma);
 
 /*
- * Convert from scene luminance in nits to PQ.
+ * Convert from scene luminance to PQ.
+ *
+ * [0.0, 1.0] range in and out.
  */
+float pqOetf(float e);
 Color pqOetf(Color e);
 
 /*
  * Convert from PQ to scene luminance in nits.
+ *
+ * [0.0, 1.0] range in and out.
  */
+float pqInvOetf(float e_gamma);
 Color pqInvOetf(Color e_gamma);
 
 
@@ -230,36 +253,38 @@
 Color applyRecovery(Color e, float recovery, float hdr_ratio);
 
 /*
- * Helper for sampling from images.
+ * Helper for sampling from YUV 420 images.
  */
 Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
 
 /*
- * Helper for sampling from images.
+ * Helper for sampling from P010 images.
+ *
+ * Expect narrow-range image data for P010.
  */
 Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
 
 /*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ */
+Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ *
+ * Expect narrow-range image data for P010.
+ */
+Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
  * Sample the recovery value for the map from a given x,y coordinate on a scale
  * that is map scale factor larger than the map size.
  */
 float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
 
 /*
- * Sample the image Y value at the provided location, with a weighting based on nearby pixels
- * and the map scale factor.
- *
- * Expect narrow-range image data for P010.
- */
-Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-
-/*
- * Sample the image Y value at the provided location, with a weighting based on nearby pixels
- * and the map scale factor. Assumes narrow-range image data for P010.
- */
-Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-
-/*
  * Convert from Color to RGBA1010102.
  *
  * Alpha always set to 1.0.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
new file mode 100644
index 0000000..e35f2d7
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+
+#include <stdint.h>
+#include <cstdio>
+
+
+namespace android::recoverymap {
+
+struct jpegr_metadata;
+
+/*
+ * Parses XMP packet and fills metadata with data from XMP
+ *
+ * @param xmp_data pointer to XMP packet
+ * @param xmp_size size of XMP packet
+ * @param metadata place to store HDR metadata values
+ * @return true if metadata is successfully retrieved, false otherwise
+*/
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+
+}
+
+#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index c1fb6c3..0185e55 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -20,8 +20,15 @@
 
 #include <errno.h>
 #include <setjmp.h>
+#include <string>
+
+using namespace std;
 
 namespace android::recoverymap {
+
+const uint32_t kExifMarker = JPEG_APP0 + 1;
+const uint32_t kICCMarker = JPEG_APP0 + 2;
+
 struct jpegr_source_mgr : jpeg_source_mgr {
     jpegr_source_mgr(const uint8_t* ptr, int len);
     ~jpegr_source_mgr();
@@ -88,6 +95,7 @@
     }
 
     mResultBuffer.clear();
+    mXMPBuffer.clear();
     if (!decode(image, length)) {
         return false;
     }
@@ -103,6 +111,15 @@
     return mResultBuffer.size();
 }
 
+void* JpegDecoder::getXMPPtr() {
+    return mXMPBuffer.data();
+}
+
+size_t JpegDecoder::getXMPSize() {
+    return mXMPBuffer.size();
+}
+
+
 size_t JpegDecoder::getDecompressedImageWidth() {
     return mWidth;
 }
@@ -115,6 +132,8 @@
     jpeg_decompress_struct cinfo;
     jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
     jpegrerror_mgr myerr;
+    string nameSpace = "http://ns.adobe.com/xap/1.0/";
+
     cinfo.err = jpeg_std_error(&myerr.pub);
     myerr.pub.error_exit = jpegrerror_exit;
 
@@ -124,9 +143,26 @@
     }
     jpeg_create_decompress(&cinfo);
 
+    jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
+
     cinfo.src = &mgr;
     jpeg_read_header(&cinfo, TRUE);
 
+    // Save XMP Data
+    for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
+        if (marker->marker == kExifMarker) {
+            const unsigned int len = marker->data_length;
+            if (len > nameSpace.size() &&
+                !strncmp(reinterpret_cast<const char*>(marker->data),
+                         nameSpace.c_str(), nameSpace.size())) {
+                mXMPBuffer.resize(len+1, 0);
+                memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
+                break;
+            }
+        }
+    }
+
+
     mWidth = cinfo.image_width;
     mHeight = cinfo.image_height;
 
@@ -161,6 +197,43 @@
     return decompressYUV(cinfo, dest);
 }
 
+bool JpegDecoder::getCompressedImageParameters(const void* image, int length,
+                              size_t *pWidth, size_t *pHeight,
+                              std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) {
+    jpeg_decompress_struct cinfo;
+    jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+    jpegrerror_mgr myerr;
+    cinfo.err = jpeg_std_error(&myerr.pub);
+    myerr.pub.error_exit = jpegrerror_exit;
+
+    if (setjmp(myerr.setjmp_buffer)) {
+        jpeg_destroy_decompress(&cinfo);
+        return false;
+    }
+    jpeg_create_decompress(&cinfo);
+
+    jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
+    jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF);
+
+    cinfo.src = &mgr;
+    if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+        jpeg_destroy_decompress(&cinfo);
+        return false;
+    }
+
+    *pWidth = cinfo.image_width;
+    *pHeight = cinfo.image_height;
+
+    //TODO: Parse iccProfile and exifData
+    (void)iccData;
+    (void)exifData;
+
+
+    jpeg_destroy_decompress(&cinfo);
+    return true;
+}
+
+
 bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
 
     JSAMPROW y[kCompressBatchSize];
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index ad41839..c9ac921 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -18,15 +18,23 @@
 #include <jpegrecoverymap/jpegencoder.h>
 #include <jpegrecoverymap/jpegdecoder.h>
 #include <jpegrecoverymap/recoverymapmath.h>
+#include <jpegrecoverymap/recoverymaputils.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/xml/xml_writer.h>
+#include <image_io/jpeg/jpeg_info.h>
+#include <image_io/jpeg/jpeg_scanner.h>
+#include <image_io/jpeg/jpeg_info_builder.h>
+#include <image_io/base/data_segment_data_source.h>
+#include <utils/Log.h>
 
 #include <memory>
 #include <sstream>
 #include <string>
+#include <cmath>
 
 using namespace std;
+using namespace photos_editing_formats::image_io;
 
 namespace android::recoverymap {
 
@@ -88,6 +96,59 @@
   return NO_ERROR;
 }
 
+/* Encode API-0 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+                                  jpegr_transfer_function hdr_tf,
+                                  jr_compressed_ptr dest,
+                                  int quality,
+                                  jr_exif_ptr /* exif */) {
+  if (uncompressed_p010_image == nullptr || dest == nullptr) {
+    return ERROR_JPEGR_INVALID_NULL_PTR;
+  }
+
+  if (quality < 0 || quality > 100) {
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  jpegr_metadata metadata;
+  metadata.version = kJpegrVersion;
+  metadata.transferFunction = hdr_tf;
+  if (hdr_tf == JPEGR_TF_PQ) {
+    metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+  }
+
+  jpegr_uncompressed_struct uncompressed_yuv_420_image;
+  JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
+
+  jpegr_uncompressed_struct map;
+  JPEGR_CHECK(generateRecoveryMap(
+      &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+  std::unique_ptr<uint8_t[]> map_data;
+  map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+  jpegr_compressed_struct compressed_map;
+  compressed_map.maxLength = map.width * map.height;
+  unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+  compressed_map.data = compressed_map_data.get();
+  JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+  JpegEncoder jpeg_encoder;
+  // TODO: determine ICC data based on color gamut information
+  if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
+                                  uncompressed_yuv_420_image.width,
+                                  uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+    return ERROR_JPEGR_ENCODE_ERROR;
+  }
+  jpegr_compressed_struct jpeg;
+  jpeg.data = jpeg_encoder.getCompressedImagePtr();
+  jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
+
+  return NO_ERROR;
+}
+
+/* Encode API-1 */
 status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_uncompressed_ptr uncompressed_yuv_420_image,
                                   jpegr_transfer_function hdr_tf,
@@ -144,6 +205,7 @@
   return NO_ERROR;
 }
 
+/* Encode API-2 */
 status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_uncompressed_ptr uncompressed_yuv_420_image,
                                   jr_compressed_ptr compressed_jpeg_image,
@@ -185,6 +247,7 @@
   return NO_ERROR;
 }
 
+/* Encode API-3 */
 status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_compressed_ptr compressed_jpeg_image,
                                   jpegr_transfer_function hdr_tf,
@@ -234,17 +297,41 @@
   return NO_ERROR;
 }
 
+status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+                                   jr_info_ptr jpegr_info) {
+  if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
+    return ERROR_JPEGR_INVALID_NULL_PTR;
+  }
+
+  jpegr_compressed_struct primary_image, recovery_map;
+  JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
+                                                &primary_image, &recovery_map));
+
+  JpegDecoder jpeg_decoder;
+  if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
+                                                 &jpegr_info->width, &jpegr_info->height,
+                                                 jpegr_info->iccData, jpegr_info->exifData)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  return NO_ERROR;
+}
+
+/* Decode API */
 status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                                   jr_uncompressed_ptr dest,
-                                  jr_exif_ptr /* exif */,
-                                  bool /* request_sdr */) {
+                                  jr_exif_ptr exif,
+                                  bool request_sdr) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
+  // TODO: fill EXIF data
+  (void) exif;
+
   jpegr_compressed_struct compressed_map;
   jpegr_metadata metadata;
-  JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map, &metadata));
+  JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
 
   jpegr_uncompressed_struct map;
   JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
@@ -259,7 +346,19 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+  if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
+                                       jpeg_decoder.getXMPSize(), &metadata)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  if (request_sdr) {
+    memcpy(dest->data, uncompressed_yuv_420_image.data,
+            uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
+    dest->width = uncompressed_yuv_420_image.width;
+    dest->height = uncompressed_yuv_420_image.height;
+  } else {
+    JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+  }
 
   return NO_ERROR;
 }
@@ -346,12 +445,15 @@
   map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
 
   ColorTransformFn hdrInvOetf = nullptr;
+  float hdr_white_nits = 0.0f;
   switch (metadata->transferFunction) {
     case JPEGR_TF_HLG:
       hdrInvOetf = hlgInvOetf;
+      hdr_white_nits = kHlgMaxNits;
       break;
     case JPEGR_TF_PQ:
       hdrInvOetf = pqInvOetf;
+      hdr_white_nits = kPqMaxNits;
       break;
   }
 
@@ -382,7 +484,7 @@
       Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
       Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
       hdr_rgb = hdrGamutConversionFn(hdr_rgb);
-      float hdr_y_nits = luminanceFn(hdr_rgb);
+      float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
 
       hdr_y_nits_avg += hdr_y_nits;
       if (hdr_y_nits > hdr_y_nits_max) {
@@ -404,13 +506,13 @@
                                          kMapDimensionScaleFactor, x, y);
       Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
       Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
-      float sdr_y_nits = luminanceFn(sdr_rgb);
+      float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
 
       Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
       Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
       Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
       hdr_rgb = hdrGamutConversionFn(hdr_rgb);
-      float hdr_y_nits = luminanceFn(hdr_rgb);
+      float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
 
       size_t pixel_idx =  x + y * map_width;
       reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
@@ -467,19 +569,66 @@
       reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
     }
   }
+  return NO_ERROR;
+}
+
+status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+                                               jr_compressed_ptr primary_image,
+                                               jr_compressed_ptr recovery_map) {
+  if (compressed_jpegr_image == nullptr) {
+    return ERROR_JPEGR_INVALID_NULL_PTR;
+  }
+
+  MessageHandler msg_handler;
+  std::shared_ptr<DataSegment> seg =
+                  DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
+                                      static_cast<const uint8_t*>(compressed_jpegr_image->data),
+                                      DataSegment::BufferDispositionPolicy::kDontDelete);
+  DataSegmentDataSource data_source(seg);
+  JpegInfoBuilder jpeg_info_builder;
+  jpeg_info_builder.SetImageLimit(2);
+  JpegScanner jpeg_scanner(&msg_handler);
+  jpeg_scanner.Run(&data_source, &jpeg_info_builder);
+  data_source.Reset();
+
+  if (jpeg_scanner.HasError()) {
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  const auto& jpeg_info = jpeg_info_builder.GetInfo();
+  const auto& image_ranges = jpeg_info.GetImageRanges();
+  if (image_ranges.empty()) {
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  if (image_ranges.size() != 2) {
+    // Must be 2 JPEG Images
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  if (primary_image != nullptr) {
+    primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+                                               image_ranges[0].GetBegin();
+    primary_image->length = image_ranges[0].GetLength();
+  }
+
+  if (recovery_map != nullptr) {
+    recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+                                              image_ranges[1].GetBegin();
+    recovery_map->length = image_ranges[1].GetLength();
+  }
 
   return NO_ERROR;
 }
 
+
 status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
-                                         jr_compressed_ptr dest,
-                                         jr_metadata_ptr metadata) {
-  if (compressed_jpegr_image == nullptr || dest == nullptr || metadata == nullptr) {
+                                         jr_compressed_ptr dest) {
+  if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  // TBD
-  return NO_ERROR;
+  return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
 }
 
 status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
@@ -493,15 +642,17 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  string xmp = generateXmp(compressed_recovery_map->length, *metadata);
-  string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+  const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
+  const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+  const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
 
   // 2 bytes: APP1 sign (ff e1)
-  // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0"
+  // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
   // x bytes: length of xmp packet
-  int length = 2 + nameSpace.size() + xmp.size();
-  uint8_t lengthH = ((length >> 8) & 0xff);
-  uint8_t lengthL = (length & 0xff);
+
+  const int length = 3 + nameSpaceLength + xmp.size();
+  const uint8_t lengthH = ((length >> 8) & 0xff);
+  const uint8_t lengthL = (length & 0xff);
 
   int pos = 0;
 
@@ -519,7 +670,7 @@
   JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
   JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
   JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
-  JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpace.size(), pos));
+  JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
   JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
   JPEGR_CHECK(Write(dest,
       (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
@@ -580,4 +731,20 @@
   return ss.str();
 }
 
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+                              jr_uncompressed_ptr dest) {
+  if (uncompressed_p010_image == nullptr || dest == nullptr) {
+    return ERROR_JPEGR_INVALID_NULL_PTR;
+  }
+
+  dest->width = uncompressed_p010_image->width;
+  dest->height = uncompressed_p010_image->height;
+  unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
+  dest->data = dest_data.get();
+
+  // TODO: Tone map algorighm here.
+
+  return NO_ERROR;
+}
+
 } // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 6dcbca3..e838f43 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -23,12 +23,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 // sRGB transformations
 
-static const float kSrgbR = 0.299f, kSrgbG = 0.587f, kSrgbB = 0.114f;
+// See IEC 61966-2-1, Equation F.7.
+static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
 
 float srgbLuminance(Color e) {
   return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
 }
 
+// See ECMA TR/98, Section 7.
 static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f;
 
 Color srgbYuvToRgb(Color e_gamma) {
@@ -37,15 +39,18 @@
              e_gamma.y + kSrgbBCb * e_gamma.u }}};
 }
 
+// See ECMA TR/98, Section 7.
+static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f;
 static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f;
 static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f;
 
 Color srgbRgbToYuv(Color e_gamma) {
-  return {{{ kSrgbR * e_gamma.r + kSrgbG * e_gamma.g + kSrgbB * e_gamma.b,
+  return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b,
              kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b,
              kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}};
 }
 
+// See IEC 61966-2-1, Equations F.5 and F.6.
 float srgbInvOetf(float e_gamma) {
   if (e_gamma <= 0.04045f) {
     return e_gamma / 12.92f;
@@ -64,7 +69,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Display-P3 transformations
 
-static const float kP3R = 0.22897f, kP3G = 0.69174f, kP3B = 0.07929f;
+// See SMPTE EG 432-1, Table 7-2.
+static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
 
 float p3Luminance(Color e) {
   return kP3R * e.r + kP3G * e.g + kP3B * e.b;
@@ -74,12 +80,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 // BT.2100 transformations - according to ITU-R BT.2100-2
 
+// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
 static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
 
 float bt2100Luminance(Color e) {
   return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
 }
 
+// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
 static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
 
 Color bt2100RgbToYuv(Color e_gamma) {
@@ -89,9 +97,9 @@
              (e_gamma.r - y_gamma) / kBt2100Cr }}};
 }
 
-// Derived from the reverse of bt2100RgbToYuv. The derivation for R and B are
-// pretty straight forward; we just reverse the formulas for U and V above. But
-// deriving the formula for G is a bit more complicated:
+// Derived by inversing bt2100RgbToYuv. The derivation for R and B are  pretty
+// straight forward; we just invert the formulas for U and V above. But deriving
+// the formula for G is a bit more complicated:
 //
 // Start with equation for luminance:
 //   Y = kBt2100R * R + kBt2100G * G + kBt2100B * B
@@ -119,9 +127,10 @@
              e_gamma.y + kBt2100Cb * e_gamma.u }}};
 }
 
+// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
 static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
 
-static float hlgOetf(float e) {
+float hlgOetf(float e) {
   if (e <= 1.0f/12.0f) {
     return sqrt(3.0f * e);
   } else {
@@ -133,7 +142,8 @@
   return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
 }
 
-static float hlgInvOetf(float e_gamma) {
+// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF.
+float hlgInvOetf(float e_gamma) {
   if (e_gamma <= 0.5f) {
     return pow(e_gamma, 2.0f) / 3.0f;
   } else {
@@ -147,13 +157,14 @@
              hlgInvOetf(e_gamma.b) }}};
 }
 
+// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
 static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
 static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
                    kPqC3 = 2392.0f / 4096.0f * 32.0f;
 
-static float pqOetf(float e) {
-  if (e < 0.0f) e = 0.0f;
-  return pow((kPqC1 + kPqC2 * pow(e / 10000.0f, kPqM1)) / (1 + kPqC3 * pow(e / 10000.0f, kPqM1)),
+float pqOetf(float e) {
+  if (e <= 0.0f) return 0.0f;
+  return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)),
              kPqM2);
 }
 
@@ -161,10 +172,18 @@
   return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
 }
 
-static float pqInvOetf(float e_gamma) {
-  static const float kPqInvOetfCoef = log2(-(pow(kPqM1, 1.0f / kPqM2) - kPqC1)
-                                         / (kPqC3 * pow(kPqM1, 1.0f / kPqM2) - kPqC2));
-  return kPqInvOetfCoef / log2(e_gamma * 10000.0f);
+// Derived from the inverse of the Reference PQ OETF.
+static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f,
+                   kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f;
+
+float pqInvOetf(float e_gamma) {
+  // This equation blows up if e_gamma is 0.0, and checking on <= 0.0 doesn't
+  // always catch 0.0. So, check on 0.0001, since anything this small will
+  // effectively be crushed to zero anyways.
+  if (e_gamma <= 0.0001f) return 0.0f;
+  return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB)
+           / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
+             kPqInvE);
 }
 
 Color pqInvOetf(Color e_gamma) {
@@ -217,7 +236,7 @@
 // TODO: confirm we always want to convert like this before calculating
 // luminance.
 ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
-    switch (sdr_gamut) {
+  switch (sdr_gamut) {
     case JPEGR_COLORGAMUT_BT709:
       switch (hdr_gamut) {
         case JPEGR_COLORGAMUT_BT709:
@@ -269,13 +288,14 @@
     gain = y_hdr / y_sdr;
   }
 
-  if (gain < -hdr_ratio) gain = -hdr_ratio;
+  if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio;
   if (gain > hdr_ratio) gain = hdr_ratio;
 
   return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f  + 127.5f);
 }
 
 static float applyRecovery(float e, float recovery, float hdr_ratio) {
+  if (e <= 0.0f) return 0.0f;
   return exp2(log2(e) + recovery * log2(hdr_ratio));
 }
 
@@ -285,45 +305,6 @@
              applyRecovery(e.b, recovery, hdr_ratio) }}};
 }
 
-// TODO: do we need something more clever for filtering either the map or images
-// to generate the map?
-
-static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
-  return val < low ? low : (high < val ? high : val);
-}
-
-static float mapUintToFloat(uint8_t map_uint) {
-  return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
-}
-
-float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
-  float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
-  float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
-
-  size_t x_lower = static_cast<size_t>(floor(x_map));
-  size_t x_upper = x_lower + 1;
-  size_t y_lower = static_cast<size_t>(floor(y_map));
-  size_t y_upper = y_lower + 1;
-
-  x_lower = clamp(x_lower, 0, map->width - 1);
-  x_upper = clamp(x_upper, 0, map->width - 1);
-  y_lower = clamp(y_lower, 0, map->height - 1);
-  y_upper = clamp(y_upper, 0, map->height - 1);
-
-  float x_influence = x_map - static_cast<float>(x_lower);
-  float y_influence = y_map - static_cast<float>(y_lower);
-
-  float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
-  float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
-  float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
-  float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
-
-  return e1 * (x_influence + y_influence) / 2.0f
-      + e2 * (x_influence + 1.0f - y_influence) / 2.0f
-      + e3 * (1.0f - x_influence + y_influence) / 2.0f
-      + e4 * (1.0f - x_influence + 1.0f - y_influence) / 2.0f;
-}
-
 Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
   size_t pixel_count = image->width * image->height;
 
@@ -382,6 +363,70 @@
   return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
 }
 
+// TODO: do we need something more clever for filtering either the map or images
+// to generate the map?
+
+static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
+  return val < low ? low : (high < val ? high : val);
+}
+
+static float mapUintToFloat(uint8_t map_uint) {
+  return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
+}
+
+static float pythDistance(float x_diff, float y_diff) {
+  return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f));
+}
+
+float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
+  float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
+  float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
+
+  size_t x_lower = static_cast<size_t>(floor(x_map));
+  size_t x_upper = x_lower + 1;
+  size_t y_lower = static_cast<size_t>(floor(y_map));
+  size_t y_upper = y_lower + 1;
+
+  x_lower = clamp(x_lower, 0, map->width - 1);
+  x_upper = clamp(x_upper, 0, map->width - 1);
+  y_lower = clamp(y_lower, 0, map->height - 1);
+  y_upper = clamp(y_upper, 0, map->height - 1);
+
+  // Use Shepard's method for inverse distance weighting. For more information:
+  // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
+
+  float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
+  float e1_dist = pythDistance(x_map - static_cast<float>(x_lower),
+                               y_map - static_cast<float>(y_lower));
+  if (e1_dist == 0.0f) return e1;
+
+  float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
+  float e2_dist = pythDistance(x_map - static_cast<float>(x_lower),
+                               y_map - static_cast<float>(y_upper));
+  if (e2_dist == 0.0f) return e2;
+
+  float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
+  float e3_dist = pythDistance(x_map - static_cast<float>(x_upper),
+                               y_map - static_cast<float>(y_lower));
+  if (e3_dist == 0.0f) return e3;
+
+  float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
+  float e4_dist = pythDistance(x_map - static_cast<float>(x_upper),
+                               y_map - static_cast<float>(y_upper));
+  if (e4_dist == 0.0f) return e2;
+
+  float e1_weight = 1.0f / e1_dist;
+  float e2_weight = 1.0f / e2_dist;
+  float e3_weight = 1.0f / e3_dist;
+  float e4_weight = 1.0f / e4_dist;
+  float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
+
+  return e1 * (e1_weight / total_weight)
+       + e2 * (e2_weight / total_weight)
+       + e3 * (e3_weight / total_weight)
+       + e4 * (e4_weight / total_weight);
+}
+
 uint32_t colorToRgba1010102(Color e_gamma) {
   return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
        | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
new file mode 100644
index 0000000..fe46cba
--- /dev/null
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2022 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 <jpegrecoverymap/recoverymaputils.h>
+#include <jpegrecoverymap/recoverymap.h>
+#include <image_io/xml/xml_reader.h>
+#include <image_io/base/message_handler.h>
+#include <image_io/xml/xml_element_rules.h>
+#include <image_io/xml/xml_handler.h>
+#include <image_io/xml/xml_rule.h>
+
+#include <string>
+#include <sstream>
+
+using namespace photos_editing_formats::image_io;
+using namespace std;
+
+namespace android::recoverymap {
+
+
+// Extremely simple XML Handler - just searches for interesting elements
+class XMPXmlHandler : public XmlHandler {
+public:
+
+    XMPXmlHandler() : XmlHandler() {
+        rangeScalingFactorState = NotStrarted;
+    }
+
+    enum ParseState {
+        NotStrarted,
+        Started,
+        Done
+    };
+
+    virtual DataMatchResult StartElement(const XmlTokenContext& context) {
+        string val;
+        if (context.BuildTokenValue(&val)) {
+            if (!val.compare(rangeScalingFactorName)) {
+                rangeScalingFactorState = Started;
+            } else {
+                if (rangeScalingFactorState != Done) {
+                    rangeScalingFactorState = NotStrarted;
+                }
+            }
+        }
+        return context.GetResult();
+    }
+
+    virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
+        if (rangeScalingFactorState == Started) {
+            rangeScalingFactorState = Done;
+        }
+        return context.GetResult();
+    }
+
+    virtual DataMatchResult ElementContent(const XmlTokenContext& context) {
+        string val;
+        if (rangeScalingFactorState == Started) {
+            if (context.BuildTokenValue(&val)) {
+                rangeScalingFactorStr.assign(val);
+            }
+        }
+        return context.GetResult();
+    }
+
+    bool getRangeScalingFactor(float* scaling_factor) {
+        if (rangeScalingFactorState == Done) {
+            stringstream ss(rangeScalingFactorStr);
+            float val;
+            if (ss >> val) {
+                *scaling_factor = val;
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    bool getTransferFunction(jpegr_transfer_function* transfer_function) {
+        *transfer_function = JPEGR_TF_HLG;
+        return true;
+    }
+
+private:
+    static const string rangeScalingFactorName;
+    string              rangeScalingFactorStr;
+    ParseState          rangeScalingFactorState;
+};
+
+const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor";
+
+
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
+    string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+
+    if (xmp_size < nameSpace.size()+2) {
+        // Data too short
+        return false;
+    }
+
+    if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
+        // Not correct namespace
+        return false;
+    }
+
+    // Position the pointers to the start of XMP XML portion
+    xmp_data += nameSpace.size()+1;
+    xmp_size -= nameSpace.size()+1;
+    XMPXmlHandler handler;
+
+    // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
+    while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
+        xmp_size--;
+    }
+
+    string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
+    MessageHandler msg_handler;
+    unique_ptr<XmlRule> rule(new XmlElementRule);
+    XmlReader reader(&handler, &msg_handler);
+    reader.StartParse(std::move(rule));
+    reader.Parse(str);
+    reader.FinishParse();
+    if (reader.HasErrors()) {
+        // Parse error
+        return false;
+    }
+
+    if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
+        return false;
+    }
+
+    if (!handler.getTransferFunction(&metadata->transferFunction)) {
+        return false;
+    }
+    return true;
+}
+
+} // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 8f37954..b509478 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -26,13 +26,15 @@
     test_suites: ["device-tests"],
     srcs: [
         "recoverymap_test.cpp",
+        "recoverymapmath_test.cpp",
     ],
     shared_libs: [
-        "libimage_io",
         "libjpeg",
         "liblog",
     ],
     static_libs: [
+        "libimage_io",
+        "libgmock",
         "libgtest",
         "libjpegdecoder",
         "libjpegencoder",
diff --git a/libs/jpegrecoverymap/tests/data/jpeg_image.jpg b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
new file mode 100644
index 0000000..e285742
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
new file mode 100644
index 0000000..01673bf
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index b3cd37e..0f96723 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -14,8 +14,19 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
 #include <jpegrecoverymap/recoverymap.h>
+#include <fcntl.h>
+#include <fstream>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
+#define RAW_P010_IMAGE_WIDTH 1280
+#define RAW_P010_IMAGE_HEIGHT 720
+#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
+
+#define SAVE_ENCODING_RESULT true
+#define SAVE_DECODING_RESULT true
 
 namespace android::recoverymap {
 
@@ -26,17 +37,55 @@
 protected:
   virtual void SetUp();
   virtual void TearDown();
+
+  struct jpegr_uncompressed_struct mRawP010Image;
+  struct jpegr_compressed_struct mJpegImage;
 };
 
 RecoveryMapTest::RecoveryMapTest() {}
 RecoveryMapTest::~RecoveryMapTest() {}
 
 void RecoveryMapTest::SetUp() {}
-void RecoveryMapTest::TearDown() {}
+void RecoveryMapTest::TearDown() {
+  free(mRawP010Image.data);
+  free(mJpegImage.data);
+}
+
+static size_t getFileSize(int fd) {
+  struct stat st;
+  if (fstat(fd, &st) < 0) {
+    ALOGW("%s : fstat failed", __func__);
+    return 0;
+  }
+  return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], void*& result, int* fileLength) {
+  int fd = open(filename, O_CLOEXEC);
+  if (fd < 0) {
+    return false;
+  }
+  int length = getFileSize(fd);
+  if (length == 0) {
+    close(fd);
+    return false;
+  }
+  if (fileLength != nullptr) {
+    *fileLength = length;
+  }
+  result = malloc(length);
+  if (read(fd, result, length) != static_cast<ssize_t>(length)) {
+    close(fd);
+    return false;
+  }
+  close(fd);
+  return true;
+}
 
 TEST_F(RecoveryMapTest, build) {
   // Force all of the recovery map lib to be linked by calling all public functions.
   RecoveryMap recovery_map;
+  recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
   recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
                            nullptr, 0, nullptr);
   recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
@@ -45,4 +94,113 @@
   recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
 }
 
+TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+  int ret;
+
+  // Load input files.
+  if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+  }
+  mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
+  mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+  RecoveryMap recoveryMap;
+
+  jpegr_compressed_struct jpegR;
+  jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+  jpegR.data = malloc(jpegR.maxLength);
+  ret = recoveryMap.encodeJPEGR(
+      &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+  if (ret != OK) {
+    FAIL() << "Error code is " << ret;
+  }
+  if (SAVE_ENCODING_RESULT) {
+    // Output image data to file
+    std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+    if (!imageFile.is_open()) {
+      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+    }
+    imageFile.write((const char*)jpegR.data, jpegR.length);
+  }
+
+  jpegr_uncompressed_struct decodedJpegR;
+  int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+  decodedJpegR.data = malloc(decodedJpegRSize);
+  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  if (ret != OK) {
+    FAIL() << "Error code is " << ret;
+  }
+  if (SAVE_DECODING_RESULT) {
+    // Output image data to file
+    std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+    if (!imageFile.is_open()) {
+      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+    }
+    imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+  }
+
+  free(jpegR.data);
+  free(decodedJpegR.data);
+}
+
+TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
+  int ret;
+
+  // Load input files.
+  if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+  }
+  mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
+  mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+  if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+    FAIL() << "Load file " << JPEG_IMAGE << " failed";
+  }
+  mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+  RecoveryMap recoveryMap;
+
+  jpegr_compressed_struct jpegR;
+  jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+  jpegR.data = malloc(jpegR.maxLength);
+  ret = recoveryMap.encodeJPEGR(
+      &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+  if (ret != OK) {
+    FAIL() << "Error code is " << ret;
+  }
+  if (SAVE_ENCODING_RESULT) {
+    // Output image data to file
+    std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+    if (!imageFile.is_open()) {
+      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+    }
+    imageFile.write((const char*)jpegR.data, jpegR.length);
+  }
+
+  jpegr_uncompressed_struct decodedJpegR;
+  int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+  decodedJpegR.data = malloc(decodedJpegRSize);
+  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  if (ret != OK) {
+    FAIL() << "Error code is " << ret;
+  }
+  if (SAVE_DECODING_RESULT) {
+    // Output image data to file
+    std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+    if (!imageFile.is_open()) {
+      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+    }
+    imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+  }
+
+  free(jpegR.data);
+  free(decodedJpegR.data);
+}
+
 } // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
new file mode 100644
index 0000000..169201c
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -0,0 +1,882 @@
+/*
+ * Copyright 2022 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 <cmath>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+
+namespace android::recoverymap {
+
+class RecoveryMapMathTest : public testing::Test {
+public:
+  RecoveryMapMathTest();
+  ~RecoveryMapMathTest();
+
+  float ComparisonEpsilon() { return 1e-4f; }
+  float LuminanceEpsilon() { return 1e-2f; }
+
+  Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
+      return {{{ static_cast<float>(y) / 255.0f,
+                 (static_cast<float>(u) - 128.0f) / 255.0f,
+                 (static_cast<float>(v) - 128.0f) / 255.0f }}};
+  }
+
+  Color P010(uint16_t y, uint16_t u, uint16_t v) {
+      return {{{ static_cast<float>(y) / 940.0f,
+                 (static_cast<float>(u) - 64.0f) / 940.0f - 0.5f,
+                 (static_cast<float>(v) - 64.0f) / 940.0f - 0.5f }}};
+  }
+
+  float Map(uint8_t e) {
+    return (static_cast<float>(e) - 127.5f) / 127.5f;
+  }
+
+  Color ColorMin(Color e1, Color e2) {
+    return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}};
+  }
+
+  Color ColorMax(Color e1, Color e2) {
+    return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}};
+  }
+
+  Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+  Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
+
+  Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+  Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
+  Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
+
+  Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+  Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+
+  Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; }
+  Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; }
+  Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; }
+
+  Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
+  Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
+  Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
+
+  float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
+    Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+    Color rgb = srgbInvOetf(rgb_gamma);
+    float luminance_scaled = luminanceFn(rgb);
+    return luminance_scaled * kSdrWhiteNits;
+  }
+
+  float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
+                             ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
+                             float scale_factor) {
+    Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
+    Color rgb = hdrInvOetf(rgb_gamma);
+    rgb = gamutConversionFn(rgb);
+    float luminance_scaled = luminanceFn(rgb);
+    return luminance_scaled * scale_factor;
+  }
+
+  Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+    Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+    Color rgb = srgbInvOetf(rgb_gamma);
+    return applyRecovery(rgb, recovery, range_scaling_factor);
+  }
+
+  jpegr_uncompressed_struct Yuv420Image() {
+    static uint8_t pixels[] = {
+      // Y
+      0x00, 0x10, 0x20, 0x30,
+      0x01, 0x11, 0x21, 0x31,
+      0x02, 0x12, 0x22, 0x32,
+      0x03, 0x13, 0x23, 0x33,
+      // U
+      0xA0, 0xA1,
+      0xA2, 0xA3,
+      // V
+      0xB0, 0xB1,
+      0xB2, 0xB3,
+    };
+    return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+  }
+
+  Color (*Yuv420Colors())[4] {
+    static Color colors[4][4] = {
+      {
+        Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
+        Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
+      }, {
+        Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
+        Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
+      }, {
+        Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
+        Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
+      }, {
+        Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
+        Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
+      },
+    };
+    return colors;
+  }
+
+  jpegr_uncompressed_struct P010Image() {
+    static uint16_t pixels[] = {
+      // Y
+      0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
+      0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
+      0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
+      0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
+      // UV
+      0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
+      0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
+    };
+    return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+  }
+
+  Color (*P010Colors())[4] {
+    static Color colors[4][4] = {
+      {
+        P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
+        P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
+      }, {
+        P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
+        P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
+      }, {
+        P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
+        P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
+      }, {
+        P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
+        P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
+      },
+    };
+    return colors;
+  }
+
+  jpegr_uncompressed_struct MapImage() {
+    static uint8_t pixels[] = {
+      0x00, 0x10, 0x20, 0x30,
+      0x01, 0x11, 0x21, 0x31,
+      0x02, 0x12, 0x22, 0x32,
+      0x03, 0x13, 0x23, 0x33,
+    };
+    return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED };
+  }
+
+  float (*MapValues())[4] {
+    static float values[4][4] = {
+      {
+        Map(0x00), Map(0x10), Map(0x20), Map(0x30),
+      }, {
+        Map(0x01), Map(0x11), Map(0x21), Map(0x31),
+      }, {
+        Map(0x02), Map(0x12), Map(0x22), Map(0x32),
+      }, {
+        Map(0x03), Map(0x13), Map(0x23), Map(0x33),
+      },
+    };
+    return values;
+  }
+
+protected:
+  virtual void SetUp();
+  virtual void TearDown();
+};
+
+RecoveryMapMathTest::RecoveryMapMathTest() {}
+RecoveryMapMathTest::~RecoveryMapMathTest() {}
+
+void RecoveryMapMathTest::SetUp() {}
+void RecoveryMapMathTest::TearDown() {}
+
+#define EXPECT_RGB_EQ(e1, e2)       \
+  EXPECT_FLOAT_EQ((e1).r, (e2).r);  \
+  EXPECT_FLOAT_EQ((e1).g, (e2).g);  \
+  EXPECT_FLOAT_EQ((e1).b, (e2).b)
+
+#define EXPECT_RGB_NEAR(e1, e2)                     \
+  EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
+  EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
+  EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
+
+#define EXPECT_RGB_CLOSE(e1, e2)                            \
+  EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
+  EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
+  EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
+
+#define EXPECT_YUV_EQ(e1, e2)       \
+  EXPECT_FLOAT_EQ((e1).y, (e2).y);  \
+  EXPECT_FLOAT_EQ((e1).u, (e2).u);  \
+  EXPECT_FLOAT_EQ((e1).v, (e2).v)
+
+#define EXPECT_YUV_NEAR(e1, e2)                     \
+  EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
+  EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
+  EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
+
+#define EXPECT_YUV_BETWEEN(e, min, max)                                           \
+  EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \
+  EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \
+  EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v)))
+
+// TODO: a bunch of these tests can be parameterized.
+
+TEST_F(RecoveryMapMathTest, ColorConstruct) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  EXPECT_FLOAT_EQ(e1.r, 0.1f);
+  EXPECT_FLOAT_EQ(e1.g, 0.2f);
+  EXPECT_FLOAT_EQ(e1.b, 0.3f);
+
+  EXPECT_FLOAT_EQ(e1.y, 0.1f);
+  EXPECT_FLOAT_EQ(e1.u, 0.2f);
+  EXPECT_FLOAT_EQ(e1.v, 0.3f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddColor) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 + e1;
+  EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+  e2 += e1;
+  EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddFloat) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 + 0.1f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
+
+  e2 += 0.1f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractColor) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 - e1;
+  EXPECT_FLOAT_EQ(e2.r, 0.0f);
+  EXPECT_FLOAT_EQ(e2.g, 0.0f);
+  EXPECT_FLOAT_EQ(e2.b, 0.0f);
+
+  e2 -= e1;
+  EXPECT_FLOAT_EQ(e2.r, -e1.r);
+  EXPECT_FLOAT_EQ(e2.g, -e1.g);
+  EXPECT_FLOAT_EQ(e2.b, -e1.b);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractFloat) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 - 0.1f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
+
+  e2 -= 0.1f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorMultiplyFloat) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 * 2.0f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+  e2 *= 2.0f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorDivideFloat) {
+  Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+  Color e2 = e1 / 2.0f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
+
+  e2 /= 2.0f;
+  EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
+  EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
+  EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbLuminance) {
+  EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
+  EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
+  EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
+  EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
+  EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbYuvToRgb) {
+  Color rgb_black = srgbYuvToRgb(YuvBlack());
+  EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+  Color rgb_white = srgbYuvToRgb(YuvWhite());
+  EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+  Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
+  EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+  Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
+  EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+  Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
+  EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbToYuv) {
+  Color yuv_black = srgbRgbToYuv(RgbBlack());
+  EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+  Color yuv_white = srgbRgbToYuv(RgbWhite());
+  EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+  Color yuv_r = srgbRgbToYuv(RgbRed());
+  EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
+
+  Color yuv_g = srgbRgbToYuv(RgbGreen());
+  EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
+
+  Color yuv_b = srgbRgbToYuv(RgbBlue());
+  EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbYuvRoundtrip) {
+  Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
+  EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+  Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
+  EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+  Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
+  EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+  Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
+  EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+  Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
+  EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbTransferFunction) {
+  EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
+  EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
+  EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
+  EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, P3Luminance) {
+  EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
+  EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
+  EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
+  EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
+  EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100Luminance) {
+  EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
+  EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
+  EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
+  EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
+  EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100YuvToRgb) {
+  Color rgb_black = bt2100YuvToRgb(YuvBlack());
+  EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+  Color rgb_white = bt2100YuvToRgb(YuvWhite());
+  EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+  Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
+  EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+  Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
+  EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+  Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
+  EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbToYuv) {
+  Color yuv_black = bt2100RgbToYuv(RgbBlack());
+  EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+  Color yuv_white = bt2100RgbToYuv(RgbWhite());
+  EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+  Color yuv_r = bt2100RgbToYuv(RgbRed());
+  EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
+
+  Color yuv_g = bt2100RgbToYuv(RgbGreen());
+  EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
+
+  Color yuv_b = bt2100RgbToYuv(RgbBlue());
+  EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbYuvRoundtrip) {
+  Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
+  EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+  Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
+  EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+  Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
+  EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+  Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
+  EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+  Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
+  EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, HlgOetf) {
+  EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
+  EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
+
+  Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
+  Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
+  EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, HlgInvOetf) {
+  EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
+  EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
+
+  Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
+  Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
+  EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, HlgTransferFunctionRoundtrip) {
+  EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
+  EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
+  EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, PqOetf) {
+  EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
+  EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
+  EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
+  EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
+
+  Color e = {{{ 0.01f, 0.5f, 0.99f }}};
+  Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
+  EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, PqInvOetf) {
+  EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
+  EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
+  EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
+  EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
+
+  Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
+  Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
+  EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) {
+  EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
+  EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
+  EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
+  EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
+  EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorConversionLookup) {
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709),
+            identityConversion);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3),
+            p3ToBt709);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100),
+            bt2100ToBt709);
+
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709),
+            bt709ToP3);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3),
+            identityConversion);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100),
+            bt2100ToP3);
+
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709),
+            bt709ToBt2100);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3),
+            p3ToBt2100);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100),
+            identityConversion);
+
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3),
+            nullptr);
+  EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100),
+            nullptr);
+}
+
+TEST_F(RecoveryMapMathTest, EncodeRecovery) {
+  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127);
+  EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0);
+  EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0);
+
+  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127);
+  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255);
+  EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255);
+  EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0);
+  EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191);
+  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63);
+
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255);
+  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191);
+  EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63);
+
+  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255);
+  EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191);
+  EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63);
+}
+
+TEST_F(RecoveryMapMathTest, ApplyRecovery) {
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack());
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack());
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack());
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f);
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f);
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f);
+
+  Color e = {{{ 0.0f, 0.5f, 1.0f }}};
+
+  EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
+  jpegr_uncompressed_struct image = Yuv420Image();
+  Color (*colors)[4] = Yuv420Colors();
+
+  for (size_t y = 0; y < 4; ++y) {
+    for (size_t x = 0; x < 4; ++x) {
+      EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
+    }
+  }
+}
+
+TEST_F(RecoveryMapMathTest, GetP010Pixel) {
+  jpegr_uncompressed_struct image = P010Image();
+  Color (*colors)[4] = P010Colors();
+
+  for (size_t y = 0; y < 4; ++y) {
+    for (size_t x = 0; x < 4; ++x) {
+      EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
+    }
+  }
+}
+
+TEST_F(RecoveryMapMathTest, SampleYuv420) {
+  jpegr_uncompressed_struct image = Yuv420Image();
+  Color (*colors)[4] = Yuv420Colors();
+
+  static const size_t kMapScaleFactor = 2;
+  for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+    for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+      Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+      Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+      for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+        for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+          Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+          min = ColorMin(min, e);
+          max = ColorMax(max, e);
+        }
+      }
+
+      // Instead of reimplementing the sampling algorithm, confirm that the
+      // sample output is within the range of the min and max of the nearest
+      // points.
+      EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
+    }
+  }
+}
+
+TEST_F(RecoveryMapMathTest, SampleP010) {
+  jpegr_uncompressed_struct image = P010Image();
+  Color (*colors)[4] = P010Colors();
+
+  static const size_t kMapScaleFactor = 2;
+  for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+    for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+      Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+      Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+      for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+        for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+          Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+          min = ColorMin(min, e);
+          max = ColorMax(max, e);
+        }
+      }
+
+      // Instead of reimplementing the sampling algorithm, confirm that the
+      // sample output is within the range of the min and max of the nearest
+      // points.
+      EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
+    }
+  }
+}
+
+TEST_F(RecoveryMapMathTest, SampleMap) {
+  jpegr_uncompressed_struct image = MapImage();
+  float (*values)[4] = MapValues();
+
+  static const size_t kMapScaleFactor = 2;
+  for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
+    for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
+      size_t x_base = x / kMapScaleFactor;
+      size_t y_base = y / kMapScaleFactor;
+
+      float min = 1.0f;
+      float max = -1.0f;
+
+      min = fmin(min, values[y_base][x_base]);
+      max = fmax(max, values[y_base][x_base]);
+      if (y_base + 1 < 4) {
+        min = fmin(min, values[y_base + 1][x_base]);
+        max = fmax(max, values[y_base + 1][x_base]);
+      }
+      if (x_base + 1 < 4) {
+        min = fmin(min, values[y_base][x_base + 1]);
+        max = fmax(max, values[y_base][x_base + 1]);
+      }
+      if (y_base + 1 < 4 && x_base + 1 < 4) {
+        min = fmin(min, values[y_base + 1][x_base + 1]);
+        max = fmax(max, values[y_base + 1][x_base + 1]);
+      }
+
+      // Instead of reimplementing the sampling algorithm, confirm that the
+      // sample output is within the range of the min and max of the nearest
+      // points.
+      EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
+                  testing::AllOf(testing::Ge(min), testing::Le(max)));
+    }
+  }
+}
+
+TEST_F(RecoveryMapMathTest, ColorToRgba1010102) {
+  EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
+  EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
+  EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
+  EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
+  EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
+
+  Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+  EXPECT_EQ(colorToRgba1010102(e_gamma),
+            0x3 << 30
+          | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
+          | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
+          | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) {
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
+                  0.0f);
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
+                  kSdrWhiteNits);
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
+              srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
+              srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
+              srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbP3) {
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
+                  0.0f);
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
+                  kSdrWhiteNits);
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
+              p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
+              p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
+              p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbBt2100) {
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
+                  0.0f);
+  EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
+                  kSdrWhiteNits);
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
+              bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
+              bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+  EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
+              bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceHlg) {
+  EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
+                                       bt2100Luminance, kHlgMaxNits),
+                  0.0f);
+  EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
+                                       bt2100Luminance, kHlgMaxNits),
+                  kHlgMaxNits);
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
+                                   bt2100Luminance, kHlgMaxNits),
+              bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
+                                   bt2100Luminance, kHlgMaxNits),
+              bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
+                                   bt2100Luminance, kHlgMaxNits),
+              bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) {
+  EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
+                                       bt2100Luminance, kPqMaxNits),
+                  0.0f);
+  EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
+                                       bt2100Luminance, kPqMaxNits),
+                  kPqMaxNits);
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
+                                       bt2100Luminance, kPqMaxNits),
+              bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
+                                       bt2100Luminance, kPqMaxNits),
+              bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
+  EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
+                                       bt2100Luminance, kPqMaxNits),
+              bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
+}
+
+//Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+TEST_F(RecoveryMapMathTest, ApplyMap) {
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f),
+                RgbWhite() * 8.0f);
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f),
+                RgbBlack());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f),
+                  RgbRed() * 8.0f);
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f),
+                  RgbGreen() * 8.0f);
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f),
+                  RgbBlue() * 8.0f);
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f),
+                RgbWhite() * sqrt(8.0f));
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f),
+                RgbBlack());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f),
+                  RgbRed() * sqrt(8.0f));
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f),
+                  RgbGreen() * sqrt(8.0f));
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f),
+                  RgbBlue() * sqrt(8.0f));
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f),
+                RgbWhite());
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f),
+                RgbBlack());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f),
+                  RgbRed());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f),
+                  RgbGreen());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f),
+                  RgbBlue());
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f),
+                RgbWhite() / sqrt(8.0f));
+  EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f),
+                RgbBlack());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f),
+                  RgbRed() / sqrt(8.0f));
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f),
+                  RgbGreen() / sqrt(8.0f));
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f),
+                  RgbBlue() / sqrt(8.0f));
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f),
+                RgbWhite() / 8.0f);
+  EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f),
+                RgbBlack());
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f),
+                  RgbRed() / 8.0f);
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f),
+                  RgbGreen() / 8.0f);
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f),
+                  RgbBlue() / 8.0f);
+}
+
+} // namespace android::recoverymap
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 539cbaa..e64165f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
 #include <android-base/thread_annotations.h>
 #include <android/gui/ISurfaceComposer.h>
-#include <gui/DisplayEventDispatcher.h>
 #include <jni.h>
+#include <nativedisplay/Choreographer.h>
 #include <private/android/choreographer.h>
 #include <utils/Looper.h>
 #include <utils/Timers.h>
@@ -31,444 +28,9 @@
 #include <queue>
 #include <thread>
 
-namespace {
-struct {
-    // Global JVM that is provided by zygote
-    JavaVM* jvm = nullptr;
-    struct {
-        jclass clazz;
-        jmethodID getInstance;
-        jmethodID registerNativeChoreographerForRefreshRateCallbacks;
-        jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
-    } displayManagerGlobal;
-} gJni;
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
 
-// Gets the JNIEnv* for this thread, and performs one-off initialization if we
-// have never retrieved a JNIEnv* pointer before.
-JNIEnv* getJniEnv() {
-    if (gJni.jvm == nullptr) {
-        ALOGW("AChoreographer: No JVM provided!");
-        return nullptr;
-    }
-
-    JNIEnv* env = nullptr;
-    if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
-        ALOGD("Attaching thread to JVM for AChoreographer");
-        JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
-        jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
-        if (attachResult != JNI_OK) {
-            ALOGE("Unable to attach thread. Error: %d", attachResult);
-            return nullptr;
-        }
-    }
-    if (env == nullptr) {
-        ALOGW("AChoreographer: No JNI env available!");
-    }
-    return env;
-}
-
-inline const char* toString(bool value) {
-    return value ? "true" : "false";
-}
-} // namespace
-
-namespace android {
-using gui::VsyncEventData;
-
-struct FrameCallback {
-    AChoreographer_frameCallback callback;
-    AChoreographer_frameCallback64 callback64;
-    AChoreographer_vsyncCallback vsyncCallback;
-    void* data;
-    nsecs_t dueTime;
-
-    inline bool operator<(const FrameCallback& rhs) const {
-        // Note that this is intentionally flipped because we want callbacks due sooner to be at
-        // the head of the queue
-        return dueTime > rhs.dueTime;
-    }
-};
-
-struct RefreshRateCallback {
-    AChoreographer_refreshRateCallback callback;
-    void* data;
-    bool firstCallbackFired = false;
-};
-
-class Choreographer;
-
-/**
- * Implementation of AChoreographerFrameCallbackData.
- */
-struct ChoreographerFrameCallbackDataImpl {
-    int64_t frameTimeNanos{0};
-
-    VsyncEventData vsyncEventData;
-
-    const Choreographer* choreographer;
-};
-
-struct {
-    std::mutex lock;
-    std::vector<Choreographer*> ptrs GUARDED_BY(lock);
-    std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
-    bool registeredToDisplayManager GUARDED_BY(lock) = false;
-
-    std::atomic<nsecs_t> mLastKnownVsync = -1;
-} gChoreographers;
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
-    explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
-    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
-                                  AChoreographer_frameCallback64 cb64,
-                                  AChoreographer_vsyncCallback vsyncCallback, void* data,
-                                  nsecs_t delay);
-    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
-            EXCLUDES(gChoreographers.lock);
-    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
-    // Drains the queue of pending vsync periods and dispatches refresh rate
-    // updates to callbacks.
-    // The assumption is that this method is only called on a single
-    // processing thread, either by looper or by AChoreographer_handleEvents
-    void handleRefreshRateUpdates();
-    void scheduleLatestConfigRequest();
-
-    enum {
-        MSG_SCHEDULE_CALLBACKS = 0,
-        MSG_SCHEDULE_VSYNC = 1,
-        MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
-    };
-    virtual void handleMessage(const Message& message) override;
-
-    static Choreographer* getForThread();
-    virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
-    int64_t getFrameInterval() const;
-    bool inCallback() const;
-
-private:
-    Choreographer(const Choreographer&) = delete;
-
-    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
-                       VsyncEventData vsyncEventData) override;
-    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
-                             nsecs_t vsyncPeriod) override;
-    void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
-    void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                    std::vector<FrameRateOverride> overrides) override;
-
-    void scheduleCallbacks();
-
-    ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
-    void registerStartTime() const;
-
-    std::mutex mLock;
-    // Protected by mLock
-    std::priority_queue<FrameCallback> mFrameCallbacks;
-    std::vector<RefreshRateCallback> mRefreshRateCallbacks;
-
-    nsecs_t mLatestVsyncPeriod = -1;
-    VsyncEventData mLastVsyncEventData;
-    bool mInCallback = false;
-
-    const sp<Looper> mLooper;
-    const std::thread::id mThreadId;
-
-    // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
-    static constexpr size_t kMaxStartTimes = 250;
-};
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
-    if (gChoreographer == nullptr) {
-        sp<Looper> looper = Looper::getForThread();
-        if (!looper.get()) {
-            ALOGW("No looper prepared for thread");
-            return nullptr;
-        }
-        gChoreographer = new Choreographer(looper);
-        status_t result = gChoreographer->initialize();
-        if (result != OK) {
-            ALOGW("Failed to initialize");
-            return nullptr;
-        }
-    }
-    return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper)
-      : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
-        mLooper(looper),
-        mThreadId(std::this_thread::get_id()) {
-    std::lock_guard<std::mutex> _l(gChoreographers.lock);
-    gChoreographers.ptrs.push_back(this);
-}
-
-Choreographer::~Choreographer() {
-    std::lock_guard<std::mutex> _l(gChoreographers.lock);
-    gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
-                                              gChoreographers.ptrs.end(),
-                                              [=](Choreographer* c) { return c == this; }),
-                               gChoreographers.ptrs.end());
-    // Only poke DisplayManagerGlobal to unregister if we previously registered
-    // callbacks.
-    if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
-        gChoreographers.registeredToDisplayManager = false;
-        JNIEnv* env = getJniEnv();
-        if (env == nullptr) {
-            ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
-            return;
-        }
-        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
-                                                  gJni.displayManagerGlobal.getInstance);
-        if (dmg == nullptr) {
-            ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
-        } else {
-            env->CallVoidMethod(dmg,
-                                gJni.displayManagerGlobal
-                                        .unregisterNativeChoreographerForRefreshRateCallbacks);
-            env->DeleteLocalRef(dmg);
-        }
-    }
-}
-
-void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
-                                             AChoreographer_frameCallback64 cb64,
-                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
-                                             nsecs_t delay) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
-    {
-        std::lock_guard<std::mutex> _l{mLock};
-        mFrameCallbacks.push(callback);
-    }
-    if (callback.dueTime <= now) {
-        if (std::this_thread::get_id() != mThreadId) {
-            if (mLooper != nullptr) {
-                Message m{MSG_SCHEDULE_VSYNC};
-                mLooper->sendMessage(this, m);
-            } else {
-                scheduleVsync();
-            }
-        } else {
-            scheduleVsync();
-        }
-    } else {
-        if (mLooper != nullptr) {
-            Message m{MSG_SCHEDULE_CALLBACKS};
-            mLooper->sendMessageDelayed(delay, this, m);
-        } else {
-            scheduleCallbacks();
-        }
-    }
-}
-
-void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
-    std::lock_guard<std::mutex> _l{mLock};
-    for (const auto& callback : mRefreshRateCallbacks) {
-        // Don't re-add callbacks.
-        if (cb == callback.callback && data == callback.data) {
-            return;
-        }
-    }
-    mRefreshRateCallbacks.emplace_back(
-            RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
-    bool needsRegistration = false;
-    {
-        std::lock_guard<std::mutex> _l2(gChoreographers.lock);
-        needsRegistration = !gChoreographers.registeredToDisplayManager;
-    }
-    if (needsRegistration) {
-        JNIEnv* env = getJniEnv();
-        if (env == nullptr) {
-            ALOGW("JNI environment is unavailable, skipping registration");
-            return;
-        }
-        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
-                                                  gJni.displayManagerGlobal.getInstance);
-        if (dmg == nullptr) {
-            ALOGW("DMS is not initialized yet: skipping registration");
-            return;
-        } else {
-            env->CallVoidMethod(dmg,
-                                gJni.displayManagerGlobal
-                                        .registerNativeChoreographerForRefreshRateCallbacks,
-                                reinterpret_cast<int64_t>(this));
-            env->DeleteLocalRef(dmg);
-            {
-                std::lock_guard<std::mutex> _l2(gChoreographers.lock);
-                gChoreographers.registeredToDisplayManager = true;
-            }
-        }
-    } else {
-        scheduleLatestConfigRequest();
-    }
-}
-
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
-                                                  void* data) {
-    std::lock_guard<std::mutex> _l{mLock};
-    mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
-                                               mRefreshRateCallbacks.end(),
-                                               [&](const RefreshRateCallback& callback) {
-                                                   return cb == callback.callback &&
-                                                           data == callback.data;
-                                               }),
-                                mRefreshRateCallbacks.end());
-}
-
-void Choreographer::scheduleLatestConfigRequest() {
-    if (mLooper != nullptr) {
-        Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
-        mLooper->sendMessage(this, m);
-    } else {
-        // If the looper thread is detached from Choreographer, then refresh rate
-        // changes will be handled in AChoreographer_handlePendingEvents, so we
-        // need to wake up the looper thread by writing to the write-end of the
-        // socket the looper is listening on.
-        // Fortunately, these events are small so sending packets across the
-        // socket should be atomic across processes.
-        DisplayEventReceiver::Event event;
-        event.header =
-                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
-                                                    PhysicalDisplayId::fromPort(0), systemTime()};
-        injectEvent(event);
-    }
-}
-
-void Choreographer::scheduleCallbacks() {
-    const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    nsecs_t dueTime;
-    {
-        std::lock_guard<std::mutex> _l{mLock};
-        // If there are no pending callbacks then don't schedule a vsync
-        if (mFrameCallbacks.empty()) {
-            return;
-        }
-        dueTime = mFrameCallbacks.top().dueTime;
-    }
-
-    if (dueTime <= now) {
-        ALOGV("choreographer %p ~ scheduling vsync", this);
-        scheduleVsync();
-        return;
-    }
-}
-
-void Choreographer::handleRefreshRateUpdates() {
-    std::vector<RefreshRateCallback> callbacks{};
-    const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
-    const nsecs_t lastPeriod = mLatestVsyncPeriod;
-    if (pendingPeriod > 0) {
-        mLatestVsyncPeriod = pendingPeriod;
-    }
-    {
-        std::lock_guard<std::mutex> _l{mLock};
-        for (auto& cb : mRefreshRateCallbacks) {
-            callbacks.push_back(cb);
-            cb.firstCallbackFired = true;
-        }
-    }
-
-    for (auto& cb : callbacks) {
-        if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
-            cb.callback(pendingPeriod, cb.data);
-        }
-    }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
-                                  VsyncEventData vsyncEventData) {
-    std::vector<FrameCallback> callbacks{};
-    {
-        std::lock_guard<std::mutex> _l{mLock};
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
-            callbacks.push_back(mFrameCallbacks.top());
-            mFrameCallbacks.pop();
-        }
-    }
-    mLastVsyncEventData = vsyncEventData;
-    for (const auto& cb : callbacks) {
-        if (cb.vsyncCallback != nullptr) {
-            const ChoreographerFrameCallbackDataImpl frameCallbackData =
-                    createFrameCallbackData(timestamp);
-            registerStartTime();
-            mInCallback = true;
-            cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
-                                     &frameCallbackData),
-                             cb.data);
-            mInCallback = false;
-        } else if (cb.callback64 != nullptr) {
-            cb.callback64(timestamp, cb.data);
-        } else if (cb.callback != nullptr) {
-            cb.callback(timestamp, cb.data);
-        }
-    }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
-    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
-          to_string(displayId).c_str(), toString(connected));
-}
-
-void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
-    LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
-}
-
-void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
-                                               std::vector<FrameRateOverride>) {
-    LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
-}
-
-void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
-    ALOGV("choreographer %p ~ received null event.", this);
-    handleRefreshRateUpdates();
-}
-
-void Choreographer::handleMessage(const Message& message) {
-    switch (message.what) {
-        case MSG_SCHEDULE_CALLBACKS:
-            scheduleCallbacks();
-            break;
-        case MSG_SCHEDULE_VSYNC:
-            scheduleVsync();
-            break;
-        case MSG_HANDLE_REFRESH_RATE_UPDATES:
-            handleRefreshRateUpdates();
-            break;
-    }
-}
-
-int64_t Choreographer::getFrameInterval() const {
-    return mLastVsyncEventData.frameInterval;
-}
-
-bool Choreographer::inCallback() const {
-    return mInCallback;
-}
-
-ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
-    return {.frameTimeNanos = timestamp,
-            .vsyncEventData = mLastVsyncEventData,
-            .choreographer = this};
-}
-
-void Choreographer::registerStartTime() const {
-    std::scoped_lock _l(gChoreographers.lock);
-    for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
-        while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
-            gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
-        }
-        gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
-    }
-}
-
-} // namespace android
 using namespace android;
 
 static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
@@ -488,27 +50,12 @@
 
 // Glue for private C api
 namespace android {
-void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
-    std::lock_guard<std::mutex> _l(gChoreographers.lock);
-    gChoreographers.mLastKnownVsync.store(vsyncPeriod);
-    for (auto c : gChoreographers.ptrs) {
-        c->scheduleLatestConfigRequest();
-    }
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+    Choreographer::signalRefreshRateCallbacks(vsyncPeriod);
 }
 
 void AChoreographer_initJVM(JNIEnv* env) {
-    env->GetJavaVM(&gJni.jvm);
-    // Now we need to find the java classes.
-    jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
-    gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
-    gJni.displayManagerGlobal.getInstance =
-            env->GetStaticMethodID(dmgClass, "getInstance",
-                                   "()Landroid/hardware/display/DisplayManagerGlobal;");
-    gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
-            env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
-    gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
-            env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
-                             "()V");
+    Choreographer::initJVM(env);
 }
 
 AChoreographer* AChoreographer_routeGetInstance() {
@@ -583,13 +130,7 @@
 }
 
 int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
-    std::scoped_lock _l(gChoreographers.lock);
-    const auto iter = gChoreographers.startTimes.find(vsyncId);
-    if (iter == gChoreographers.startTimes.end()) {
-        ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
-        return 0;
-    }
-    return iter->second;
+    return Choreographer::getStartTimeNanosForVsyncId(vsyncId);
 }
 
 } // namespace android
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 60328e4..bf0805b 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -117,15 +117,6 @@
 #define CHECK_NOT_NULL(name) \
     LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
 
-namespace {
-
-sp<IBinder> getToken(ADisplay* display) {
-    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
-    return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
-}
-
-} // namespace
-
 namespace android {
 
 int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
@@ -139,10 +130,9 @@
     ui::DisplayConnectionType displayConnectionTypes[size];
     int numModes = 0;
     for (int i = 0; i < size; ++i) {
-        const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-
         ui::StaticDisplayInfo staticInfo;
-        if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
+        if (const status_t status =
+                    SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
             status != OK) {
             return status;
         }
@@ -150,7 +140,7 @@
 
         ui::DynamicDisplayInfo dynamicInfo;
         if (const status_t status =
-                    SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
+                    SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo);
             status != OK) {
             return status;
         }
@@ -260,14 +250,15 @@
 int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
     CHECK_NOT_NULL(display);
 
-    sp<IBinder> token = getToken(display);
     ui::DynamicDisplayInfo info;
-    if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+    if (const auto status =
+                SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info);
         status != OK) {
         return status;
     }
 
-    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
     for (size_t i = 0; i < impl->numConfigs; i++) {
         auto* config = impl->configs + i;
         if (config->id == info.activeDisplayModeId) {
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 8d8a2bc..70de33d 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -56,6 +56,7 @@
         ":libgui_frame_event_aidl",
         "AChoreographer.cpp",
         "ADisplay.cpp",
+        "Choreographer.cpp",
         "surfacetexture/surface_texture.cpp",
         "surfacetexture/SurfaceTexture.cpp",
         "surfacetexture/ImageConsumer.cpp",
diff --git a/libs/nativedisplay/Choreographer.cpp b/libs/nativedisplay/Choreographer.cpp
new file mode 100644
index 0000000..01e9f04
--- /dev/null
+++ b/libs/nativedisplay/Choreographer.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2022 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 <jni.h>
+#include <nativedisplay/Choreographer.h>
+
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
+
+namespace {
+struct {
+    // Global JVM that is provided by zygote
+    JavaVM* jvm = nullptr;
+    struct {
+        jclass clazz;
+        jmethodID getInstance;
+        jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+        jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+    } displayManagerGlobal;
+} gJni;
+
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+    if (gJni.jvm == nullptr) {
+        ALOGW("AChoreographer: No JVM provided!");
+        return nullptr;
+    }
+
+    JNIEnv* env = nullptr;
+    if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGD("Attaching thread to JVM for AChoreographer");
+        JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+        jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+        if (attachResult != JNI_OK) {
+            ALOGE("Unable to attach thread. Error: %d", attachResult);
+            return nullptr;
+        }
+    }
+    if (env == nullptr) {
+        ALOGW("AChoreographer: No JNI env available!");
+    }
+    return env;
+}
+
+inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+} // namespace
+
+namespace android {
+
+Choreographer::Context Choreographer::gChoreographers;
+
+static thread_local Choreographer* gChoreographer;
+
+void Choreographer::initJVM(JNIEnv* env) {
+    env->GetJavaVM(&gJni.jvm);
+    // Now we need to find the java classes.
+    jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+    gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+    gJni.displayManagerGlobal.getInstance =
+            env->GetStaticMethodID(dmgClass, "getInstance",
+                                   "()Landroid/hardware/display/DisplayManagerGlobal;");
+    gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+    gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+                             "()V");
+}
+
+Choreographer* Choreographer::getForThread() {
+    if (gChoreographer == nullptr) {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            ALOGW("No looper prepared for thread");
+            return nullptr;
+        }
+        gChoreographer = new Choreographer(looper);
+        status_t result = gChoreographer->initialize();
+        if (result != OK) {
+            ALOGW("Failed to initialize");
+            return nullptr;
+        }
+    }
+    return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper)
+      : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
+        mLooper(looper),
+        mThreadId(std::this_thread::get_id()) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+                                              gChoreographers.ptrs.end(),
+                                              [=](Choreographer* c) { return c == this; }),
+                               gChoreographers.ptrs.end());
+    // Only poke DisplayManagerGlobal to unregister if we previously registered
+    // callbacks.
+    if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+        gChoreographers.registeredToDisplayManager = false;
+        JNIEnv* env = getJniEnv();
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+            return;
+        }
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .unregisterNativeChoreographerForRefreshRateCallbacks);
+            env->DeleteLocalRef(dmg);
+        }
+    }
+}
+
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                             AChoreographer_frameCallback64 cb64,
+                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
+                                             nsecs_t delay) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        mFrameCallbacks.push(callback);
+    }
+    if (callback.dueTime <= now) {
+        if (std::this_thread::get_id() != mThreadId) {
+            if (mLooper != nullptr) {
+                Message m{MSG_SCHEDULE_VSYNC};
+                mLooper->sendMessage(this, m);
+            } else {
+                scheduleVsync();
+            }
+        } else {
+            scheduleVsync();
+        }
+    } else {
+        if (mLooper != nullptr) {
+            Message m{MSG_SCHEDULE_CALLBACKS};
+            mLooper->sendMessageDelayed(delay, this, m);
+        } else {
+            scheduleCallbacks();
+        }
+    }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+    std::lock_guard<std::mutex> _l{mLock};
+    for (const auto& callback : mRefreshRateCallbacks) {
+        // Don't re-add callbacks.
+        if (cb == callback.callback && data == callback.data) {
+            return;
+        }
+    }
+    mRefreshRateCallbacks.emplace_back(
+            RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+    bool needsRegistration = false;
+    {
+        std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+        needsRegistration = !gChoreographers.registeredToDisplayManager;
+    }
+    if (needsRegistration) {
+        JNIEnv* env = getJniEnv();
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping registration");
+            return;
+        }
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet: skipping registration");
+            return;
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .registerNativeChoreographerForRefreshRateCallbacks,
+                                reinterpret_cast<int64_t>(this));
+            env->DeleteLocalRef(dmg);
+            {
+                std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+                gChoreographers.registeredToDisplayManager = true;
+            }
+        }
+    } else {
+        scheduleLatestConfigRequest();
+    }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+                                                  void* data) {
+    std::lock_guard<std::mutex> _l{mLock};
+    mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+                                               mRefreshRateCallbacks.end(),
+                                               [&](const RefreshRateCallback& callback) {
+                                                   return cb == callback.callback &&
+                                                           data == callback.data;
+                                               }),
+                                mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+    if (mLooper != nullptr) {
+        Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+        mLooper->sendMessage(this, m);
+    } else {
+        // If the looper thread is detached from Choreographer, then refresh rate
+        // changes will be handled in AChoreographer_handlePendingEvents, so we
+        // need to wake up the looper thread by writing to the write-end of the
+        // socket the looper is listening on.
+        // Fortunately, these events are small so sending packets across the
+        // socket should be atomic across processes.
+        DisplayEventReceiver::Event event;
+        event.header =
+                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+                                                    PhysicalDisplayId::fromPort(0), systemTime()};
+        injectEvent(event);
+    }
+}
+
+void Choreographer::scheduleCallbacks() {
+    const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t dueTime;
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        // If there are no pending callbacks then don't schedule a vsync
+        if (mFrameCallbacks.empty()) {
+            return;
+        }
+        dueTime = mFrameCallbacks.top().dueTime;
+    }
+
+    if (dueTime <= now) {
+        ALOGV("choreographer %p ~ scheduling vsync", this);
+        scheduleVsync();
+        return;
+    }
+}
+
+void Choreographer::handleRefreshRateUpdates() {
+    std::vector<RefreshRateCallback> callbacks{};
+    const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+    const nsecs_t lastPeriod = mLatestVsyncPeriod;
+    if (pendingPeriod > 0) {
+        mLatestVsyncPeriod = pendingPeriod;
+    }
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        for (auto& cb : mRefreshRateCallbacks) {
+            callbacks.push_back(cb);
+            cb.firstCallbackFired = true;
+        }
+    }
+
+    for (auto& cb : callbacks) {
+        if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+            cb.callback(pendingPeriod, cb.data);
+        }
+    }
+}
+
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+                                  VsyncEventData vsyncEventData) {
+    std::vector<FrameCallback> callbacks{};
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+            callbacks.push_back(mFrameCallbacks.top());
+            mFrameCallbacks.pop();
+        }
+    }
+    mLastVsyncEventData = vsyncEventData;
+    for (const auto& cb : callbacks) {
+        if (cb.vsyncCallback != nullptr) {
+            const ChoreographerFrameCallbackDataImpl frameCallbackData =
+                    createFrameCallbackData(timestamp);
+            registerStartTime();
+            mInCallback = true;
+            cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+                                     &frameCallbackData),
+                             cb.data);
+            mInCallback = false;
+        } else if (cb.callback64 != nullptr) {
+            cb.callback64(timestamp, cb.data);
+        } else if (cb.callback != nullptr) {
+            cb.callback(timestamp, cb.data);
+        }
+    }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+          to_string(displayId).c_str(), toString(connected));
+}
+
+void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+    LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+                                               std::vector<FrameRateOverride>) {
+    LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
+}
+
+void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
+    ALOGV("choreographer %p ~ received null event.", this);
+    handleRefreshRateUpdates();
+}
+
+void Choreographer::handleMessage(const Message& message) {
+    switch (message.what) {
+        case MSG_SCHEDULE_CALLBACKS:
+            scheduleCallbacks();
+            break;
+        case MSG_SCHEDULE_VSYNC:
+            scheduleVsync();
+            break;
+        case MSG_HANDLE_REFRESH_RATE_UPDATES:
+            handleRefreshRateUpdates();
+            break;
+    }
+}
+
+int64_t Choreographer::getFrameInterval() const {
+    return mLastVsyncEventData.frameInterval;
+}
+
+bool Choreographer::inCallback() const {
+    return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+    return {.frameTimeNanos = timestamp,
+            .vsyncEventData = mLastVsyncEventData,
+            .choreographer = this};
+}
+
+void Choreographer::registerStartTime() const {
+    std::scoped_lock _l(gChoreographers.lock);
+    for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
+        while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
+            gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
+        }
+        gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+}
+
+void Choreographer::signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+    for (auto c : gChoreographers.ptrs) {
+        c->scheduleLatestConfigRequest();
+    }
+}
+
+int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
+    std::scoped_lock _l(gChoreographers.lock);
+    const auto iter = gChoreographers.startTimes.find(vsyncId);
+    if (iter == gChoreographers.startTimes.end()) {
+        ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
+        return 0;
+    }
+    return iter->second;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/nativedisplay/include/nativedisplay/Choreographer.h b/libs/nativedisplay/include/nativedisplay/Choreographer.h
new file mode 100644
index 0000000..bb63f29
--- /dev/null
+++ b/libs/nativedisplay/include/nativedisplay/Choreographer.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2022 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 <gui/DisplayEventDispatcher.h>
+#include <private/android/choreographer.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+using gui::VsyncEventData;
+
+struct FrameCallback {
+    AChoreographer_frameCallback callback;
+    AChoreographer_frameCallback64 callback64;
+    AChoreographer_vsyncCallback vsyncCallback;
+    void* data;
+    nsecs_t dueTime;
+
+    inline bool operator<(const FrameCallback& rhs) const {
+        // Note that this is intentionally flipped because we want callbacks due sooner to be at
+        // the head of the queue
+        return dueTime > rhs.dueTime;
+    }
+};
+
+struct RefreshRateCallback {
+    AChoreographer_refreshRateCallback callback;
+    void* data;
+    bool firstCallbackFired = false;
+};
+
+class Choreographer;
+
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+    int64_t frameTimeNanos{0};
+
+    VsyncEventData vsyncEventData;
+
+    const Choreographer* choreographer;
+};
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+    struct Context {
+        std::mutex lock;
+        std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+        std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
+        bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+        std::atomic<nsecs_t> mLastKnownVsync = -1;
+    };
+    static Context gChoreographers;
+
+    explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
+    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                  AChoreographer_frameCallback64 cb64,
+                                  AChoreographer_vsyncCallback vsyncCallback, void* data,
+                                  nsecs_t delay);
+    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+            EXCLUDES(gChoreographers.lock);
+    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    // Drains the queue of pending vsync periods and dispatches refresh rate
+    // updates to callbacks.
+    // The assumption is that this method is only called on a single
+    // processing thread, either by looper or by AChoreographer_handleEvents
+    void handleRefreshRateUpdates();
+    void scheduleLatestConfigRequest();
+
+    enum {
+        MSG_SCHEDULE_CALLBACKS = 0,
+        MSG_SCHEDULE_VSYNC = 1,
+        MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
+    };
+    virtual void handleMessage(const Message& message) override;
+
+    static void initJVM(JNIEnv* env);
+    static Choreographer* getForThread();
+    static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
+    static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
+    virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+    int64_t getFrameInterval() const;
+    bool inCallback() const;
+
+private:
+    Choreographer(const Choreographer&) = delete;
+
+    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+                       VsyncEventData vsyncEventData) override;
+    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+                             nsecs_t vsyncPeriod) override;
+    void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+    void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                    std::vector<FrameRateOverride> overrides) override;
+
+    void scheduleCallbacks();
+
+    ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+    void registerStartTime() const;
+
+    std::mutex mLock;
+    // Protected by mLock
+    std::priority_queue<FrameCallback> mFrameCallbacks;
+    std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+
+    nsecs_t mLatestVsyncPeriod = -1;
+    VsyncEventData mLastVsyncEventData;
+    bool mInCallback = false;
+
+    const sp<Looper> mLooper;
+    const std::thread::id mThreadId;
+
+    // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
+    static constexpr size_t kMaxStartTimes = 250;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 180dce9..bbafbff 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -702,6 +702,14 @@
     return ahardwarebuffer_format;
 }
 
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
+    GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+    auto& mapper = GraphicBufferMapper::get();
+    ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+    mapper.getDataspace(gb->handle, &dataspace);
+    return static_cast<int32_t>(dataspace);
+}
+
 uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
     using android::hardware::graphics::common::V1_1::BufferUsage;
     static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index c345385..b7b2926 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -232,15 +232,6 @@
     return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
 }
 
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
-    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
-        return -EINVAL;
-    }
-    return native_window_set_frame_rate(window, 0,
-            ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
-            ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
-}
-
 /**************************************************************************************************
  * vndk-stable
  **************************************************************************************************/
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index ddfd1d1..6d3d295 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -52,6 +52,11 @@
 // convert HAL format to AHardwareBuffer format (note: this is a no-op)
 uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
 
+// retrieves a dataspace from the AHardwareBuffer metadata, if the device
+// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
+// supported.
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
+
 // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
 uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
 
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index a27e3dd..be6623e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -372,8 +372,12 @@
  *
  * \return 0 for success, -EINVAL if the window value is invalid.
  */
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
-        __INTRODUCED_IN(__ANDROID_API_U__);
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+        __INTRODUCED_IN(__ANDROID_API_U__) {
+    return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
+            ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+            ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
 
 #ifdef __cplusplus
 }
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 76d23fa..c2fd6ef 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -51,7 +51,6 @@
     ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
     ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
-    ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
     ANativeWindow_setUsage; # llndk
@@ -71,6 +70,7 @@
       android::AHardwareBuffer_convertToPixelFormat*;
       android::AHardwareBuffer_convertFromGrallocUsageBits*;
       android::AHardwareBuffer_convertToGrallocUsageBits*;
+      android::AHardwareBuffer_getDataSpace*;
       android::AHardwareBuffer_to_GraphicBuffer*;
       android::AHardwareBuffer_to_ANativeWindowBuffer*;
       android::AHardwareBuffer_from_GraphicBuffer*;
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index d08c221..341c011 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -39,8 +39,12 @@
             ALOGD("RenderEngine with SkiaGL Backend");
             return renderengine::skia::SkiaGLRenderEngine::create(args);
         case RenderEngineType::SKIA_VK:
+#ifdef RE_SKIAVK
             ALOGD("RenderEngine with SkiaVK Backend");
             return renderengine::skia::SkiaVkRenderEngine::create(args);
+#else
+            LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
         case RenderEngineType::SKIA_GL_THREADED: {
             ALOGD("Threaded RenderEngine with SkiaGL Backend");
             return renderengine::threaded::RenderEngineThreaded::create(
@@ -50,12 +54,16 @@
                     args.renderEngineType);
         }
         case RenderEngineType::SKIA_VK_THREADED:
+#ifdef RE_SKIAVK
             ALOGD("Threaded RenderEngine with SkiaVK Backend");
             return renderengine::threaded::RenderEngineThreaded::create(
                     [args]() {
                         return android::renderengine::skia::SkiaVkRenderEngine::create(args);
                     },
                     args.renderEngineType);
+#else
+            LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
         case RenderEngineType::GLES:
         default:
             ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index f9424f0..2b8495c 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "RenderEngine"
@@ -673,3 +677,4 @@
 } // namespace skia
 } // namespace renderengine
 } // namespace android
+#endif // RE_SKIAVK
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 2e0cf45..1e42b80 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -17,6 +17,10 @@
 #ifndef SF_SKIAVKRENDERENGINE_H_
 #define SF_SKIAVKRENDERENGINE_H_
 
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
 #include <vk/GrVkBackendContext.h>
 
 #include "SkiaRenderEngine.h"
@@ -55,4 +59,5 @@
 } // namespace renderengine
 } // namespace android
 
-#endif
+#endif // RE_SKIAVK
+#endif // SF_SKIAVKRENDERENGINE_H_
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index f3f2da8..7db95a7 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -112,6 +112,7 @@
     virtual bool useColorManagement() const = 0;
 };
 
+#ifdef RE_SKIAVK
 class SkiaVkRenderEngineFactory : public RenderEngineFactory {
 public:
     std::string name() override { return "SkiaVkRenderEngineFactory"; }
@@ -152,6 +153,8 @@
 public:
     bool useColorManagement() const override { return true; }
 };
+#endif // RE_SKIAVK
+
 class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
 public:
     std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -1557,11 +1560,17 @@
     expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
 }
 
+#ifdef RE_SKIAVK
 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
                          testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
                                          std::make_shared<SkiaGLESCMRenderEngineFactory>(),
                                          std::make_shared<SkiaVkRenderEngineFactory>(),
                                          std::make_shared<SkiaVkCMRenderEngineFactory>()));
+#else  // RE_SKIAVK
+INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
+                         testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
+                                         std::make_shared<SkiaGLESCMRenderEngineFactory>()));
+#endif // RE_SKIAVK
 
 TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
     if (!GetParam()->typeSupported()) {
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 2b93c6e..b6b9cc4 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,9 +21,10 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libsensor",
 
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 78f692b..2278d39 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -42,6 +42,7 @@
     GET_DYNAMIC_SENSOR_LIST,
     CREATE_SENSOR_DIRECT_CONNECTION,
     SET_OPERATION_PARAMETER,
+    GET_RUNTIME_SENSOR_LIST,
 };
 
 class BpSensorServer : public BpInterface<ISensorServer>
@@ -90,6 +91,25 @@
         return v;
     }
 
+    virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        data.writeInt32(deviceId);
+        remote()->transact(GET_RUNTIME_SENSOR_LIST, data, &reply);
+        Sensor s;
+        Vector<Sensor> v;
+        uint32_t n = reply.readUint32();
+        v.setCapacity(n);
+        while (n) {
+            n--;
+            reply.read(s);
+            v.add(s);
+        }
+        return v;
+    }
+
     virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
              int mode, const String16& opPackageName, const String16& attributionTag)
     {
@@ -194,6 +214,18 @@
             }
             return NO_ERROR;
         }
+        case GET_RUNTIME_SENSOR_LIST: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            const int deviceId = data.readInt32();
+            Vector<Sensor> v(getRuntimeSensorList(opPackageName, deviceId));
+            size_t n = v.size();
+            reply->writeUint32(static_cast<uint32_t>(n));
+            for (size_t i = 0; i < n; i++) {
+                reply->write(v[i]);
+            }
+            return NO_ERROR;
+        }
         case CREATE_SENSOR_DIRECT_CONNECTION: {
             CHECK_INTERFACE(ISensorServer, data, reply);
             const String16& opPackageName = data.readString16();
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 0ba9704..2748276 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -201,6 +201,19 @@
     return static_cast<ssize_t>(count);
 }
 
+ssize_t SensorManager::getRuntimeSensorList(int deviceId, Vector<Sensor>& runtimeSensors) {
+    Mutex::Autolock _l(mLock);
+    status_t err = assertStateLocked();
+    if (err < 0) {
+        return static_cast<ssize_t>(err);
+    }
+
+    runtimeSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, deviceId);
+    size_t count = runtimeSensors.size();
+
+    return static_cast<ssize_t>(count);
+}
+
 ssize_t SensorManager::getDynamicSensorList(Sensor const* const** list) {
     Mutex::Autolock _l(mLock);
     status_t err = assertStateLocked();
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index ce5c672..3295196 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -43,6 +43,7 @@
 
     virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
     virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+    virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) = 0;
 
     virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
              int mode, const String16& opPackageName, const String16& attributionTag) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 8d0a8a4..0798da2 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -59,6 +59,7 @@
     ssize_t getSensorList(Sensor const* const** list);
     ssize_t getDynamicSensorList(Vector<Sensor>& list);
     ssize_t getDynamicSensorList(Sensor const* const** list);
+    ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list);
     Sensor const* getDefaultSensor(int type);
     sp<SensorEventQueue> createEventQueue(
         String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 2a4a370..42b0cc1 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -68,6 +68,9 @@
     // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
     // dataspace for correct conversion to linear colors.
     ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+
+    enum SkSLType { Shader, ColorFilter };
+    SkSLType type = Shader;
 };
 
 static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
@@ -96,6 +99,10 @@
 // 2. Apply color transform matrices in linear space
 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
 
+// Generates a shader string that applies color transforms in linear space.
+// This is intended to be plugged into an SkColorFilter
+std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);
+
 // Generates a list of uniforms to set on the LinearEffect shader above.
 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
         const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index f80e93f..a3c403e 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -386,12 +386,23 @@
     }
 }
 
-void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
-    shader.append(R"(
-        uniform shader child;
-        half4 main(float2 xy) {
-            float4 c = float4(child.eval(xy));
-    )");
+void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
+                           std::string& shader) {
+    switch (type) {
+        case LinearEffect::SkSLType::ColorFilter:
+            shader.append(R"(
+                half4 main(half4 inputColor) {
+                    float4 c = float4(inputColor);
+            )");
+            break;
+        case LinearEffect::SkSLType::Shader:
+            shader.append(R"(
+                uniform shader child;
+                half4 main(float2 xy) {
+                    float4 c = float4(child.eval(xy));
+            )");
+            break;
+    }
     if (undoPremultipliedAlpha) {
         shader.append(R"(
             c.rgb = c.rgb / (c.a + 0.0019);
@@ -459,7 +470,7 @@
     generateXYZTransforms(shaderString);
     generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
     generateOETF(linearEffect.outputDataspace, shaderString);
-    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString);
     return shaderString;
 }
 
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a2791a6..65a8769 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <type_traits>
 
+#include <ui/GraphicTypes.h>
 #include <ui/Size.h>
 #include <utils/Flattenable.h>
 #include <utils/Timers.h>
@@ -34,6 +35,7 @@
     ui::Size resolution;
     float xDpi = 0;
     float yDpi = 0;
+    std::vector<ui::Hdr> supportedHdrTypes;
 
     float refreshRate = 0;
     nsecs_t appVsyncOffset = 0;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 8c9fe4c..0b77754 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,6 +35,7 @@
     // we can't use size_t because it may have different width
     // in the client process.
     ui::DisplayModeId activeDisplayModeId;
+    float renderFrameRate;
 
     std::vector<ui::ColorMode> supportedColorModes;
     ui::ColorMode activeColorMode;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 8661c36..1775d39 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -20,6 +20,7 @@
 #include <aidl/android/hardware/graphics/common/ChromaSiting.h>
 #include <aidl/android/hardware/graphics/common/Compression.h>
 #include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/common/Interlaced.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayout.h>
 #include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -42,7 +43,6 @@
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
 /**
@@ -50,6 +50,7 @@
  */
 using aidl::android::hardware::graphics::common::BlendMode;
 using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Hdr;
 using aidl::android::hardware::graphics::common::PlaneLayout;
 using aidl::android::hardware::graphics::common::Smpte2086;
 
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 83d431d..c1d60f4 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -20,7 +20,14 @@
 
 namespace android::ui {
 
-enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+enum class Rotation {
+    Rotation0 = 0,
+    Rotation90 = 1,
+    Rotation180 = 2,
+    Rotation270 = 3,
+
+    ftl_last = Rotation270
+};
 
 // Equivalent to Surface.java constants.
 constexpr auto ROTATION_0 = Rotation::Rotation0;
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
deleted file mode 100644
index 0bda798..0000000
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "buffer_hub_queue_client.cpp",
-    "buffer_hub_queue_parcelable.cpp",
-]
-
-includeFiles = [
-    "include",
-]
-
-staticLibraries = [
-    "libbufferhub",
-]
-
-sharedLibraries = [
-    "libbinder",
-    "libcutils",
-    "liblog",
-    "libui",
-    "libutils",
-    "libpdx_default_transport",
-]
-
-headerLibraries = [
-    "libdvr_headers",
-    "libnativebase_headers",
-]
-
-cc_library_shared {
-    name: "libbufferhubqueue",
-    cflags: [
-        "-DLOG_TAG=\"libbufferhubqueue\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-Wall",
-        "-Werror",
-        "-Wno-format",
-        "-Wno-unused-parameter",
-        "-Wno-unused-variable",
-    ],
-    srcs: sourceFiles,
-    export_include_dirs: includeFiles,
-    export_static_lib_headers: staticLibraries,
-    static_libs: staticLibraries,
-    shared_libs: sharedLibraries,
-    header_libs: headerLibraries,
-}
-
-subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
deleted file mode 100644
index 2d3fa4a..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ /dev/null
@@ -1,823 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_client.h"
-
-#include <inttypes.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-
-#include <array>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <pdx/trace.h>
-
-#define RETRY_EINTR(fnc_call)                 \
-  ([&]() -> decltype(fnc_call) {              \
-    decltype(fnc_call) result;                \
-    do {                                      \
-      result = (fnc_call);                    \
-    } while (result == -1 && errno == EINTR); \
-    return result;                            \
-  })()
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-std::pair<int32_t, int32_t> Unstuff(uint64_t value) {
-  return {static_cast<int32_t>(value >> 32),
-          static_cast<int32_t>(value & ((1ull << 32) - 1))};
-}
-
-uint64_t Stuff(int32_t a, int32_t b) {
-  const uint32_t ua = static_cast<uint32_t>(a);
-  const uint32_t ub = static_cast<uint32_t>(b);
-  return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub);
-}
-
-}  // anonymous namespace
-
-BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
-    : Client{pdx::default_transport::ClientChannel::Create(
-          std::move(channel_handle))} {
-  Initialize();
-}
-
-BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
-    : Client{
-          pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} {
-  Initialize();
-}
-
-void BufferHubQueue::Initialize() {
-  int ret = epoll_fd_.Create();
-  if (ret < 0) {
-    ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
-          strerror(-ret));
-    return;
-  }
-
-  epoll_event event = {
-      .events = EPOLLIN | EPOLLET,
-      .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
-  ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
-  if (ret < 0) {
-    ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
-          strerror(-ret));
-  }
-}
-
-Status<void> BufferHubQueue::ImportQueue() {
-  auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
-  if (!status) {
-    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  } else {
-    SetupQueue(status.get());
-    return {};
-  }
-}
-
-void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) {
-  is_async_ = queue_info.producer_config.is_async;
-  default_width_ = queue_info.producer_config.default_width;
-  default_height_ = queue_info.producer_config.default_height;
-  default_format_ = queue_info.producer_config.default_format;
-  user_metadata_size_ = queue_info.producer_config.user_metadata_size;
-  id_ = queue_info.id;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle(/*silent*/ false))
-    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  else
-    return nullptr;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
-  if (auto status = CreateConsumerQueueHandle(/*silent*/ true))
-    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  else
-    return nullptr;
-}
-
-Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle(
-    bool silent) {
-  auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent);
-  if (!status) {
-    ALOGE(
-        "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
-        "%s",
-        status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  }
-
-  return status;
-}
-
-pdx::Status<ConsumerQueueParcelable>
-BufferHubQueue::CreateConsumerQueueParcelable(bool silent) {
-  auto status = CreateConsumerQueueHandle(silent);
-  if (!status)
-    return status.error_status();
-
-  // A temporary consumer queue client to pull its channel parcelable.
-  auto consumer_queue =
-      std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
-  ConsumerQueueParcelable queue_parcelable(
-      consumer_queue->GetChannel()->TakeChannelParcelable());
-
-  if (!queue_parcelable.IsValid()) {
-    ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  return {std::move(queue_parcelable)};
-}
-
-bool BufferHubQueue::WaitForBuffers(int timeout) {
-  ATRACE_NAME("BufferHubQueue::WaitForBuffers");
-  std::array<epoll_event, kMaxEvents> events;
-
-  // Loop at least once to check for hangups.
-  do {
-    ALOGD_IF(
-        TRACE,
-        "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
-        id(), count(), capacity());
-
-    // If there is already a buffer then just check for hangup without waiting.
-    const int ret = epoll_fd_.Wait(events.data(), events.size(),
-                                   count() == 0 ? timeout : 0);
-
-    if (ret == 0) {
-      ALOGI_IF(TRACE,
-               "BufferHubQueue::WaitForBuffers: No events before timeout: "
-               "queue_id=%d",
-               id());
-      return count() != 0;
-    }
-
-    if (ret < 0 && ret != -EINTR) {
-      ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
-      return false;
-    }
-
-    const int num_events = ret;
-
-    // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
-    // one for each buffer in the queue, and one extra event for the queue
-    // client itself.
-    for (int i = 0; i < num_events; i++) {
-      int32_t event_fd;
-      int32_t index;
-      std::tie(event_fd, index) = Unstuff(events[i].data.u64);
-
-      PDX_TRACE_FORMAT(
-          "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;"
-          "slot=%d|",
-          id(), num_events, i, event_fd, index);
-
-      ALOGD_IF(TRACE,
-               "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d",
-               i, event_fd, index);
-
-      if (is_buffer_event_index(index)) {
-        HandleBufferEvent(static_cast<size_t>(index), event_fd,
-                          events[i].events);
-      } else if (is_queue_event_index(index)) {
-        HandleQueueEvent(events[i].events);
-      } else {
-        ALOGW(
-            "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d "
-            "index=%d",
-            event_fd, index);
-      }
-    }
-  } while (count() == 0 && capacity() > 0 && !hung_up());
-
-  return count() != 0;
-}
-
-Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd,
-                                               int poll_events) {
-  ATRACE_NAME("BufferHubQueue::HandleBufferEvent");
-  if (!buffers_[slot]) {
-    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
-    return ErrorStatus(ENOENT);
-  }
-
-  auto status = buffers_[slot]->GetEventMask(poll_events);
-  if (!status) {
-    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  const int events = status.get();
-  PDX_TRACE_FORMAT(
-      "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;"
-      "events=%d|",
-      id(), buffers_[slot]->id(), slot, event_fd, poll_events, events);
-
-  if (events & EPOLLIN) {
-    return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()});
-  } else if (events & EPOLLHUP) {
-    ALOGW(
-        "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu "
-        "event_fd=%d buffer_id=%d",
-        slot, buffers_[slot]->event_fd(), buffers_[slot]->id());
-    return RemoveBuffer(slot);
-  } else {
-    ALOGW(
-        "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
-        "events=%d",
-        slot, events);
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) {
-  ATRACE_NAME("BufferHubQueue::HandleQueueEvent");
-  auto status = GetEventMask(poll_event);
-  if (!status) {
-    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  const int events = status.get();
-  if (events & EPOLLIN) {
-    // Note that after buffer imports, if |count()| still returns 0, epoll
-    // wait will be tried again to acquire the newly imported buffer.
-    auto buffer_status = OnBufferAllocated();
-    if (!buffer_status) {
-      ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
-            buffer_status.GetErrorMessage().c_str());
-    }
-  } else if (events & EPOLLHUP) {
-    ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
-    hung_up_ = true;
-  } else {
-    ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::AddBuffer(
-    const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
-           slot);
-
-  if (is_full()) {
-    ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
-    return ErrorStatus(E2BIG);
-  }
-
-  if (buffers_[slot]) {
-    // Replace the buffer if the slot is occupied. This could happen when the
-    // producer side replaced the slot with a newly allocated buffer. Remove the
-    // buffer before setting up with the new one.
-    auto remove_status = RemoveBuffer(slot);
-    if (!remove_status)
-      return remove_status.error_status();
-  }
-
-  for (const auto& event_source : buffer->GetEventSources()) {
-    epoll_event event = {.events = event_source.event_mask | EPOLLET,
-                         .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
-    const int ret =
-        epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
-    if (ret < 0) {
-      ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
-            strerror(-ret));
-      return ErrorStatus(-ret);
-    }
-  }
-
-  buffers_[slot] = buffer;
-  capacity_++;
-  return {};
-}
-
-Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
-  ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
-
-  if (buffers_[slot]) {
-    for (const auto& event_source : buffers_[slot]->GetEventSources()) {
-      const int ret =
-          epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
-      if (ret < 0) {
-        ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
-              strerror(-ret));
-        return ErrorStatus(-ret);
-      }
-    }
-
-    // Trigger OnBufferRemoved callback if registered.
-    if (on_buffer_removed_)
-      on_buffer_removed_(buffers_[slot]);
-
-    buffers_[slot] = nullptr;
-    capacity_--;
-  }
-
-  return {};
-}
-
-Status<void> BufferHubQueue::Enqueue(Entry entry) {
-  if (!is_full()) {
-    // Find and remove the enqueued buffer from unavailable_buffers_slot if
-    // exist.
-    auto enqueued_buffer_iter = std::find_if(
-        unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
-        [&entry](size_t slot) -> bool { return slot == entry.slot; });
-    if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
-      unavailable_buffers_slot_.erase(enqueued_buffer_iter);
-    }
-
-    available_buffers_.push(std::move(entry));
-
-    // Trigger OnBufferAvailable callback if registered.
-    if (on_buffer_available_)
-      on_buffer_available_();
-
-    return {};
-  } else {
-    ALOGE("%s: Buffer queue is full!", __FUNCTION__);
-    return ErrorStatus(E2BIG);
-  }
-}
-
-Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
-                                                               size_t* slot) {
-  ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
-
-  PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
-
-  if (count() == 0) {
-    if (!WaitForBuffers(timeout))
-      return ErrorStatus(ETIMEDOUT);
-  }
-
-  auto& entry = available_buffers_.top();
-  PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
-                   entry.slot);
-
-  std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
-  *slot = entry.slot;
-
-  available_buffers_.pop();
-  unavailable_buffers_slot_.push_back(*slot);
-
-  return {std::move(buffer)};
-}
-
-void BufferHubQueue::SetBufferAvailableCallback(
-    BufferAvailableCallback callback) {
-  on_buffer_available_ = callback;
-}
-
-void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) {
-  on_buffer_removed_ = callback;
-}
-
-pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
-  // Clear all available buffers.
-  while (!available_buffers_.empty())
-    available_buffers_.pop();
-
-  pdx::Status<void> last_error;  // No error.
-  // Clear all buffers this producer queue is tracking.
-  for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
-    if (buffers_[slot] != nullptr) {
-      auto status = RemoveBuffer(slot);
-      if (!status) {
-        ALOGE(
-            "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
-            "slot=%zu.",
-            slot);
-        last_error = status.error_status();
-      }
-    }
-  }
-
-  return last_error;
-}
-
-ProducerQueue::ProducerQueue(LocalChannelHandle handle)
-    : BASE(std::move(handle)) {
-  auto status = ImportQueue();
-  if (!status) {
-    ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-  }
-}
-
-ProducerQueue::ProducerQueue(const ProducerQueueConfig& config,
-                             const UsagePolicy& usage)
-    : BASE(BufferHubRPC::kClientPath) {
-  auto status =
-      InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage);
-  if (!status) {
-    ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-    return;
-  }
-
-  SetupQueue(status.get());
-}
-
-Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
-    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-    uint64_t usage, size_t buffer_count) {
-  if (buffer_count == 0) {
-    return {std::vector<size_t>()};
-  }
-
-  if (capacity() + buffer_count > kMaxQueueCapacity) {
-    ALOGE(
-        "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
-        "allocate %zu more buffer(s).",
-        capacity(), buffer_count);
-    return ErrorStatus(E2BIG);
-  }
-
-  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
-          width, height, layer_count, format, usage, buffer_count);
-  if (!status) {
-    ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto buffer_handle_slots = status.take();
-  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count,
-                      "BufferHubRPC::ProducerQueueAllocateBuffers should "
-                      "return %zu buffer handle(s), but returned %zu instead.",
-                      buffer_count, buffer_handle_slots.size());
-
-  std::vector<size_t> buffer_slots;
-  buffer_slots.reserve(buffer_count);
-
-  // Bookkeeping for each buffer.
-  for (auto& hs : buffer_handle_slots) {
-    auto& buffer_handle = hs.first;
-    size_t buffer_slot = hs.second;
-
-    // Note that import might (though very unlikely) fail. If so, buffer_handle
-    // will be closed and included in returned buffer_slots.
-    if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
-                  buffer_slot)) {
-      ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
-               buffer_slot);
-      buffer_slots.push_back(buffer_slot);
-    }
-  }
-
-  if (buffer_slots.size() != buffer_count) {
-    // Error out if the count of imported buffer(s) is not correct.
-    ALOGE(
-        "ProducerQueue::AllocateBuffers: requested to import %zu "
-        "buffers, but actually imported %zu buffers.",
-        buffer_count, buffer_slots.size());
-    return ErrorStatus(ENOMEM);
-  }
-
-  return {std::move(buffer_slots)};
-}
-
-Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
-                                             uint32_t layer_count,
-                                             uint32_t format, uint64_t usage) {
-  // We only allocate one buffer at a time.
-  constexpr size_t buffer_count = 1;
-  auto status =
-      AllocateBuffers(width, height, layer_count, format, usage, buffer_count);
-  if (!status) {
-    ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return {status.get()[0]};
-}
-
-Status<void> ProducerQueue::AddBuffer(
-    const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
-           id(), buffer->id(), slot);
-  // For producer buffer, we need to enqueue the newly added buffer
-  // immediately. Producer queue starts with all buffers in available state.
-  auto status = BufferHubQueue::AddBuffer(buffer, slot);
-  if (!status)
-    return status;
-
-  return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
-}
-
-Status<size_t> ProducerQueue::InsertBuffer(
-    const std::shared_ptr<ProducerBuffer>& buffer) {
-  if (buffer == nullptr ||
-      !BufferHubDefs::isClientGained(buffer->buffer_state(),
-                                     buffer->client_state_mask())) {
-    ALOGE(
-        "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
-        "gained state.");
-    return ErrorStatus(EINVAL);
-  }
-
-  auto status_or_slot =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
-          buffer->cid());
-  if (!status_or_slot) {
-    ALOGE(
-        "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
-        "buffer_cid=%d, error: %s.",
-        buffer->cid(), status_or_slot.GetErrorMessage().c_str());
-    return status_or_slot.error_status();
-  }
-
-  size_t slot = status_or_slot.get();
-
-  // Note that we are calling AddBuffer() from the base class to explicitly
-  // avoid Enqueue() the ProducerBuffer.
-  auto status = BufferHubQueue::AddBuffer(buffer, slot);
-  if (!status) {
-    ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-  return {slot};
-}
-
-Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
-  auto status =
-      InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
-  if (!status) {
-    ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return BufferHubQueue::RemoveBuffer(slot);
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
-    int timeout, size_t* slot, LocalHandle* release_fence) {
-  DvrNativeBufferMetadata canonical_meta;
-  return Dequeue(timeout, slot, &canonical_meta, release_fence);
-}
-
-pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
-    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-    pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
-  ATRACE_NAME("ProducerQueue::Dequeue");
-  if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
-    ALOGE("%s: Invalid parameter.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  std::shared_ptr<ProducerBuffer> buffer;
-  Status<std::shared_ptr<BufferHubBase>> dequeue_status =
-      BufferHubQueue::Dequeue(timeout, slot);
-  if (dequeue_status.ok()) {
-    buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
-  } else {
-    if (gain_posted_buffer) {
-      Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
-          ProducerQueue::DequeueUnacquiredBuffer(slot);
-      if (!dequeue_unacquired_status.ok()) {
-        ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
-              dequeue_unacquired_status.error());
-        return dequeue_unacquired_status.error_status();
-      }
-      buffer = dequeue_unacquired_status.take();
-    } else {
-      return dequeue_status.error_status();
-    }
-  }
-  const int ret =
-      buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
-  if (ret < 0 && ret != -EALREADY)
-    return ErrorStatus(-ret);
-
-  return {std::move(buffer)};
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
-    size_t* slot) {
-  if (unavailable_buffers_slot_.size() < 1) {
-    ALOGE(
-        "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
-        "acquired state if exist.",
-        __FUNCTION__);
-    return ErrorStatus(ENOMEM);
-  }
-
-  // Find the first buffer that is not in acquired state from
-  // unavailable_buffers_slot_.
-  for (auto iter = unavailable_buffers_slot_.begin();
-       iter != unavailable_buffers_slot_.end(); iter++) {
-    std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
-    if (buffer == nullptr) {
-      ALOGE("%s failed. Buffer slot %d is  null.", __FUNCTION__,
-            static_cast<int>(*slot));
-      return ErrorStatus(EIO);
-    }
-    if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
-      *slot = *iter;
-      unavailable_buffers_slot_.erase(iter);
-      unavailable_buffers_slot_.push_back(*slot);
-      ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
-            __FUNCTION__, static_cast<int>(*slot));
-      return {std::move(buffer)};
-    }
-  }
-  ALOGE(
-      "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
-      __FUNCTION__);
-  return ErrorStatus(EBUSY);
-}
-
-pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
-  if (capacity() != 0) {
-    ALOGE(
-        "%s: producer queue can only be taken out as a parcelable when empty. "
-        "Current queue capacity: %zu",
-        __FUNCTION__, capacity());
-    return ErrorStatus(EINVAL);
-  }
-
-  std::unique_ptr<pdx::ClientChannel> channel = TakeChannel();
-  ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable());
-
-  // Here the queue parcelable is returned and holds the underlying system
-  // resources backing the queue; while the original client channel of this
-  // producer queue is destroyed in place so that this client can no longer
-  // provide producer operations.
-  return {std::move(queue_parcelable)};
-}
-
-/*static */
-std::unique_ptr<ConsumerQueue> ConsumerQueue::Import(
-    LocalChannelHandle handle) {
-  return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle)));
-}
-
-ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
-    : BufferHubQueue(std::move(handle)) {
-  auto status = ImportQueue();
-  if (!status) {
-    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-  }
-
-  auto import_status = ImportBuffers();
-  if (import_status) {
-    ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
-  } else {
-    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
-          import_status.GetErrorMessage().c_str());
-  }
-}
-
-Status<size_t> ConsumerQueue::ImportBuffers() {
-  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
-  if (!status) {
-    if (status.error() == EBADR) {
-      ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
-      return {0};
-    } else {
-      ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
-            status.GetErrorMessage().c_str());
-      return status.error_status();
-    }
-  }
-
-  int ret;
-  Status<void> last_error;
-  size_t imported_buffers_count = 0;
-
-  auto buffer_handle_slots = status.take();
-  for (auto& buffer_handle_slot : buffer_handle_slots) {
-    ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
-             buffer_handle_slot.first.value());
-
-    std::unique_ptr<ConsumerBuffer> consumer_buffer =
-        ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
-    if (!consumer_buffer) {
-      ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
-            buffer_handle_slot.second);
-      last_error = ErrorStatus(EPIPE);
-      continue;
-    }
-
-    auto add_status =
-        AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
-    if (!add_status) {
-      ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
-            add_status.GetErrorMessage().c_str());
-      last_error = add_status;
-    } else {
-      imported_buffers_count++;
-    }
-  }
-
-  if (imported_buffers_count > 0)
-    return {imported_buffers_count};
-  else
-    return last_error.error_status();
-}
-
-Status<void> ConsumerQueue::AddBuffer(
-    const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
-           buffer->id(), slot);
-  return BufferHubQueue::AddBuffer(buffer, slot);
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
-    int timeout, size_t* slot, void* meta, size_t user_metadata_size,
-    LocalHandle* acquire_fence) {
-  if (user_metadata_size != user_metadata_size_) {
-    ALOGE(
-        "%s: Metadata size (%zu) for the dequeuing buffer does not match "
-        "metadata size (%zu) for the queue.",
-        __FUNCTION__, user_metadata_size, user_metadata_size_);
-    return ErrorStatus(EINVAL);
-  }
-
-  DvrNativeBufferMetadata canonical_meta;
-  auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence);
-  if (!status)
-    return status.error_status();
-
-  if (meta && user_metadata_size) {
-    void* metadata_src =
-        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
-    if (metadata_src) {
-      memcpy(meta, metadata_src, user_metadata_size);
-    } else {
-      ALOGW("%s: no user-defined metadata.", __FUNCTION__);
-    }
-  }
-
-  return status;
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
-    int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-    pdx::LocalHandle* acquire_fence) {
-  ATRACE_NAME("ConsumerQueue::Dequeue");
-  if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
-    ALOGE("%s: Invalid parameter.", __FUNCTION__);
-    return ErrorStatus(EINVAL);
-  }
-
-  auto status = BufferHubQueue::Dequeue(timeout, slot);
-  if (!status)
-    return status.error_status();
-
-  auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
-  const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
-  if (ret < 0)
-    return ErrorStatus(-ret);
-
-  return {std::move(buffer)};
-}
-
-Status<void> ConsumerQueue::OnBufferAllocated() {
-  ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
-
-  auto status = ImportBuffers();
-  if (!status) {
-    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
-          status.GetErrorMessage().c_str());
-    return ErrorStatus(status.error());
-  } else if (status.get() == 0) {
-    ALOGW("%s: No new buffers allocated!", __FUNCTION__);
-    return ErrorStatus(ENOBUFS);
-  } else {
-    ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
-             status.get());
-    return {};
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
deleted file mode 100644
index f705749..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_parcelable.h"
-
-#include <binder/Parcel.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-namespace android {
-namespace dvr {
-
-template <BufferHubQueueParcelableMagic Magic>
-bool BufferHubQueueParcelable<Magic>::IsValid() const {
-  return !!channel_parcelable_ && channel_parcelable_->IsValid();
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() {
-  if (!IsValid()) {
-    ALOGE(
-        "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel.");
-    return {};  // Returns an empty channel handle.
-  }
-
-  // Take channel handle out of the parcelable and reset the parcelable.
-  pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle();
-  // Now channel_parcelable_ should already be invalid, but reset it to release
-  // the invalid parcelable object from unique_ptr.
-  channel_parcelable_ = nullptr;
-  return handle;
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const {
-  if (!IsValid()) {
-    ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel.");
-    return -EINVAL;
-  }
-
-  status_t res = parcel->writeUint32(Magic);
-  if (res != OK) {
-    ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
-    return res;
-  }
-
-  return channel_parcelable_->writeToParcel(parcel);
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) {
-  if (IsValid()) {
-    ALOGE(
-        "BufferHubQueueParcelable::readFromParcel: This parcelable object has "
-        "been initialized already.");
-    return -EINVAL;
-  }
-
-  uint32_t out_magic = 0;
-  status_t res = OK;
-
-  res = parcel->readUint32(&out_magic);
-  if (res != OK)
-    return res;
-
-  if (out_magic != Magic) {
-    ALOGE(
-        "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, "
-        "epxected: 0x%x",
-        out_magic, Magic);
-    return -EINVAL;
-  }
-
-  // (Re)Alocate channel parcelable object.
-  channel_parcelable_ =
-      std::make_unique<pdx::default_transport::ChannelParcelable>();
-  return channel_parcelable_->readFromParcel(parcel);
-}
-
-template class BufferHubQueueParcelable<
-    BufferHubQueueParcelableMagic::Producer>;
-template class BufferHubQueueParcelable<
-    BufferHubQueueParcelableMagic::Consumer>;
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
deleted file mode 100644
index 74b4b3d..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ /dev/null
@@ -1,476 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-
-#include <ui/BufferQueueDefs.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <private/dvr/producer_buffer.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <queue>
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-class ConsumerQueue;
-
-// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
-// automatically re-requeued when released by the remote side.
-class BufferHubQueue : public pdx::Client {
- public:
-  using BufferAvailableCallback = std::function<void()>;
-  using BufferRemovedCallback =
-      std::function<void(const std::shared_ptr<BufferHubBase>&)>;
-
-  virtual ~BufferHubQueue() {}
-
-  // Creates a new consumer queue that is attached to the producer. Returns
-  // a new consumer queue client or nullptr on failure.
-  std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
-
-  // Creates a new consumer queue that is attached to the producer. This queue
-  // sets each of its imported consumer buffers to the ignored state to avoid
-  // participation in lifecycle events.
-  std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
-
-  // Returns whether the buffer queue is in async mode.
-  bool is_async() const { return is_async_; }
-
-  // Returns the default buffer width of this buffer queue.
-  uint32_t default_width() const { return default_width_; }
-
-  // Returns the default buffer height of this buffer queue.
-  uint32_t default_height() const { return default_height_; }
-
-  // Returns the default buffer format of this buffer queue.
-  uint32_t default_format() const { return default_format_; }
-
-  // Creates a new consumer in handle form for immediate transport over RPC.
-  pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
-      bool silent = false);
-
-  // Creates a new consumer in parcelable form for immediate transport over
-  // Binder.
-  pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable(
-      bool silent = false);
-
-  // Returns the number of buffers avaiable for dequeue.
-  size_t count() const { return available_buffers_.size(); }
-
-  // Returns the total number of buffers that the queue is tracking.
-  size_t capacity() const { return capacity_; }
-
-  // Returns the size of metadata structure associated with this queue.
-  size_t metadata_size() const { return user_metadata_size_; }
-
-  // Returns whether the buffer queue is full.
-  bool is_full() const {
-    return available_buffers_.size() >= kMaxQueueCapacity;
-  }
-
-  // Returns whether the buffer queue is connected to bufferhubd.
-  bool is_connected() const { return !!GetChannel(); }
-
-  int GetBufferId(size_t slot) const {
-    return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
-                                                      : -1;
-  }
-
-  std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
-    return buffers_[slot];
-  }
-
-  pdx::Status<int> GetEventMask(int events) {
-    if (auto* client_channel = GetChannel()) {
-      return client_channel->GetEventMask(events);
-    } else {
-      return pdx::ErrorStatus(EINVAL);
-    }
-  }
-
-  // Returns an fd that signals pending queue events using
-  // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
-  // called to handle pending queue events.
-  int queue_fd() const { return epoll_fd_.Get(); }
-
-  // Handles any pending events, returning available buffers to the queue and
-  // reaping disconnected buffers. Returns true if successful, false if an error
-  // occurred.
-  bool HandleQueueEvents() { return WaitForBuffers(0); }
-
-  // Set buffer event callbacks, which are std::function wrappers. The caller is
-  // responsible for ensuring the validity of these callbacks' callable targets.
-  void SetBufferAvailableCallback(BufferAvailableCallback callback);
-  void SetBufferRemovedCallback(BufferRemovedCallback callback);
-
-  // The queue tracks at most this many buffers.
-  static constexpr size_t kMaxQueueCapacity =
-      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
-
-  static constexpr int kNoTimeOut = -1;
-
-  int id() const { return id_; }
-  bool hung_up() const { return hung_up_; }
-
- protected:
-  explicit BufferHubQueue(pdx::LocalChannelHandle channel);
-  explicit BufferHubQueue(const std::string& endpoint_path);
-
-  // Imports the queue parameters by querying BufferHub for the parameters for
-  // this channel.
-  pdx::Status<void> ImportQueue();
-
-  // Sets up the queue with the given parameters.
-  void SetupQueue(const QueueInfo& queue_info);
-
-  // Register a buffer for management by the queue. Used by subclasses to add a
-  // buffer to internal bookkeeping.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
-                              size_t slot);
-
-  // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
-  // to deregister a buffer for epoll and internal bookkeeping.
-  virtual pdx::Status<void> RemoveBuffer(size_t slot);
-
-  // Free all buffers that belongs to this queue. Can only be called from
-  // producer side.
-  virtual pdx::Status<void> FreeAllBuffers();
-
-  // Dequeue a buffer from the free queue, blocking until one is available. The
-  // timeout argument specifies the number of milliseconds that |Dequeue()| will
-  // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
-  // while specifying a timeout equal to zero cause Dequeue() to return
-  // immediately, even if no buffers are available.
-  pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
-                                                      size_t* slot);
-
-  // Waits for buffers to become available and adds them to the available queue.
-  bool WaitForBuffers(int timeout);
-
-  pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd,
-                                      int poll_events);
-  pdx::Status<void> HandleQueueEvent(int poll_events);
-
-  // Entry in the priority queue of available buffers that stores related
-  // per-buffer data.
-  struct Entry {
-    Entry() : slot(0) {}
-    Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
-          uint64_t in_index)
-        : buffer(in_buffer), slot(in_slot), index(in_index) {}
-    Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
-          std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
-          size_t in_slot)
-        : buffer(in_buffer),
-          metadata(std::move(in_metadata)),
-          fence(std::move(in_fence)),
-          slot(in_slot) {}
-    Entry(Entry&&) = default;
-    Entry& operator=(Entry&&) = default;
-
-    std::shared_ptr<BufferHubBase> buffer;
-    std::unique_ptr<uint8_t[]> metadata;
-    pdx::LocalHandle fence;
-    size_t slot;
-    uint64_t index;
-  };
-
-  struct EntryComparator {
-    bool operator()(const Entry& lhs, const Entry& rhs) {
-      return lhs.index > rhs.index;
-    }
-  };
-
-  // Enqueues a buffer to the available list (Gained for producer or Acquireed
-  // for consumer).
-  pdx::Status<void> Enqueue(Entry entry);
-
-  // Called when a buffer is allocated remotely.
-  virtual pdx::Status<void> OnBufferAllocated() { return {}; }
-
-  // Size of the metadata that buffers in this queue cary.
-  size_t user_metadata_size_{0};
-
-  // Buffers and related data that are available for dequeue.
-  std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
-      available_buffers_;
-
-  // Slot of the buffers that are not available for normal dequeue. For example,
-  // the slot of posted or acquired buffers in the perspective of a producer.
-  std::vector<size_t> unavailable_buffers_slot_;
-
- private:
-  void Initialize();
-
-  // Special epoll data field indicating that the epoll event refers to the
-  // queue.
-  static constexpr int64_t kEpollQueueEventIndex = -1;
-
-  static constexpr size_t kMaxEvents = 128;
-
-  // The u64 data field of an epoll event is interpreted as int64_t:
-  // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
-  // element of |buffers_| as a direct index;
-  static bool is_buffer_event_index(int64_t index) {
-    return index >= 0 &&
-           index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
-  }
-
-  // When |index| == kEpollQueueEventIndex it refers to the queue itself.
-  static bool is_queue_event_index(int64_t index) {
-    return index == BufferHubQueue::kEpollQueueEventIndex;
-  }
-
-  // Whether the buffer queue is operating in Async mode.
-  // From GVR's perspective of view, this means a buffer can be acquired
-  // asynchronously by the compositor.
-  // From Android Surface's perspective of view, this is equivalent to
-  // IGraphicBufferProducer's async mode. When in async mode, a producer
-  // will never block even if consumer is running slow.
-  bool is_async_{false};
-
-  // Default buffer width that is set during ProducerQueue's creation.
-  uint32_t default_width_{1};
-
-  // Default buffer height that is set during ProducerQueue's creation.
-  uint32_t default_height_{1};
-
-  // Default buffer format that is set during ProducerQueue's creation.
-  uint32_t default_format_{1};  // PIXEL_FORMAT_RGBA_8888
-
-  // Tracks the buffers belonging to this queue. Buffers are stored according to
-  // "slot" in this vector. Each slot is a logical id of the buffer within this
-  // queue regardless of its queue position or presence in the ring buffer.
-  std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
-
-  // Keeps track with how many buffers have been added into the queue.
-  size_t capacity_{0};
-
-  // Epoll fd used to manage buffer events.
-  EpollFileDescriptor epoll_fd_;
-
-  // Flag indicating that the other side hung up. For ProducerQueues this
-  // triggers when BufferHub dies or explicitly closes the queue channel. For
-  // ConsumerQueues this can either mean the same or that the ProducerQueue on
-  // the other end hung up.
-  bool hung_up_{false};
-
-  // Global id for the queue that is consistent across processes.
-  int id_{-1};
-
-  // Buffer event callbacks
-  BufferAvailableCallback on_buffer_available_;
-  BufferRemovedCallback on_buffer_removed_;
-
-  BufferHubQueue(const BufferHubQueue&) = delete;
-  void operator=(BufferHubQueue&) = delete;
-};
-
-class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
- public:
-  // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
-  // in |usage_clear_mask| will be automatically masked off. Note that
-  // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
-  // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
-  // allocation through this producer queue shall not have any of the usage bits
-  // in |usage_deny_set_mask| set. Allocation calls violating this will be
-  // rejected. All buffer allocation through this producer queue must have all
-  // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
-  // this will be rejected. Note that |usage_deny_set_mask| and
-  // |usage_deny_clear_mask| shall not conflict with each other. Such
-  // configuration will be treated as invalid input on creation.
-  static std::unique_ptr<ProducerQueue> Create(
-      const ProducerQueueConfig& config, const UsagePolicy& usage) {
-    return BASE::Create(config, usage);
-  }
-
-  // Import a ProducerQueue from a channel handle.
-  static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) {
-    return BASE::Create(std::move(handle));
-  }
-
-  // Get a producer buffer. Note that the method doesn't check whether the
-  // buffer slot has a valid buffer that has been allocated already. When no
-  // buffer has been imported before it returns nullptr; otherwise it returns
-  // a shared pointer to a ProducerBuffer.
-  std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<ProducerBuffer>(
-        BufferHubQueue::GetBuffer(slot));
-  }
-
-  // Batch allocate buffers. Once allocated, producer buffers are automatically
-  // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED
-  // state). Upon success, returns a list of slots for each buffer allocated.
-  pdx::Status<std::vector<size_t>> AllocateBuffers(
-      uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-      uint64_t usage, size_t buffer_count);
-
-  // Allocate producer buffer to populate the queue. Once allocated, a producer
-  // buffer is automatically enqueue'd into the ProducerQueue and available to
-  // use (i.e. in GAINED state). Upon success, returns the slot number for the
-  // buffer allocated.
-  pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height,
-                                     uint32_t layer_count, uint32_t format,
-                                     uint64_t usage);
-
-  // Add a producer buffer to populate the queue. Once added, a producer buffer
-  // is available to use (i.e. in GAINED state).
-  pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
-                              size_t slot);
-
-  // Inserts a ProducerBuffer into the queue. On success, the method returns the
-  // |slot| number where the new buffer gets inserted. Note that the buffer
-  // being inserted should be in Gain'ed state prior to the call and it's
-  // considered as already Dequeued when the function returns.
-  pdx::Status<size_t> InsertBuffer(
-      const std::shared_ptr<ProducerBuffer>& buffer);
-
-  // Remove producer buffer from the queue.
-  pdx::Status<void> RemoveBuffer(size_t slot) override;
-
-  // Free all buffers on this producer queue.
-  pdx::Status<void> FreeAllBuffers() override {
-    return BufferHubQueue::FreeAllBuffers();
-  }
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  // @return a buffer in gained state, which was originally in released state.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
-      int timeout, size_t* slot, pdx::LocalHandle* release_fence);
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  //
-  // @param timeout to dequeue a buffer.
-  // @param slot is the slot of the output ProducerBuffer.
-  // @param release_fence for gaining a buffer.
-  // @param out_meta metadata of the output buffer.
-  // @param gain_posted_buffer whether to gain posted buffer if no released
-  //     buffer is available to gain.
-  // @return a buffer in gained state, which was originally in released state if
-  //     gain_posted_buffer is false, or in posted/released state if
-  //     gain_posted_buffer is true.
-  // TODO(b/112007999): gain_posted_buffer true is only used to prevent
-  // libdvrtracking from starving when there are non-responding clients. This
-  // gain_posted_buffer param can be removed once libdvrtracking start to use
-  // the new AHardwareBuffer API.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
-      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-      pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
-
-  // Enqueues a producer buffer in the queue.
-  pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
-                            size_t slot, uint64_t index) {
-    return BufferHubQueue::Enqueue({buffer, slot, index});
-  }
-
-  // Takes out the current producer queue as a binder parcelable object. Note
-  // that the queue must be empty to be exportable. After successful export, the
-  // producer queue client should no longer be used.
-  pdx::Status<ProducerQueueParcelable> TakeAsParcelable();
-
- private:
-  friend BASE;
-
-  // Constructors are automatically exposed through ProducerQueue::Create(...)
-  // static template methods inherited from ClientBase, which take the same
-  // arguments as the constructors.
-  explicit ProducerQueue(pdx::LocalChannelHandle handle);
-  ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
-
-  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
-  // and caller should call Post() once it's done writing to release the buffer
-  // to the consumer side.
-  //
-  // @param slot the slot of the returned buffer.
-  // @return a buffer in gained state, which was originally in posted state or
-  //     released state.
-  pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
-      size_t* slot);
-};
-
-class ConsumerQueue : public BufferHubQueue {
- public:
-  // Get a consumer buffer. Note that the method doesn't check whether the
-  // buffer slot has a valid buffer that has been imported already. When no
-  // buffer has been imported before it returns nullptr; otherwise returns a
-  // shared pointer to a ConsumerBuffer.
-  std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<ConsumerBuffer>(
-        BufferHubQueue::GetBuffer(slot));
-  }
-
-  // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
-  // whether or not buffers are set to be ignored when imported. This may be
-  // used to avoid participation in the buffer lifecycle by a consumer queue
-  // that is only used to spawn other consumer queues, such as in an
-  // intermediate service.
-  static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle);
-
-  // Import newly created buffers from the service side.
-  // Returns number of buffers successfully imported or an error.
-  pdx::Status<size_t> ImportBuffers();
-
-  // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
-  // mode, and caller should call Releasse() once it's done writing to release
-  // the buffer to the producer side. |meta| is passed along from BufferHub,
-  // The user of ProducerBuffer is responsible with making sure that the
-  // Dequeue() is done with the corect metadata type and size with those used
-  // when the buffer is orignally created.
-  template <typename Meta>
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
-    return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
-  }
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
-    return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
-  }
-
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, void* meta, size_t user_metadata_size,
-      pdx::LocalHandle* acquire_fence);
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
-      int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-      pdx::LocalHandle* acquire_fence);
-
- private:
-  friend BufferHubQueue;
-
-  explicit ConsumerQueue(pdx::LocalChannelHandle handle);
-
-  // Add a consumer buffer to populate the queue. Once added, a consumer buffer
-  // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
-  // will catch the |Post| and |Acquire| the buffer to make it available for
-  // consumer.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
-                              size_t slot);
-
-  pdx::Status<void> OnBufferAllocated() override;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
deleted file mode 100644
index 36ab5f6..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/channel_parcelable.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-namespace android {
-namespace dvr {
-
-enum BufferHubQueueParcelableMagic : uint32_t {
-  Producer = 0x62687170,  // 'bhqp'
-  Consumer = 0x62687163,  // 'bhqc'
-};
-
-template <BufferHubQueueParcelableMagic Magic>
-class BufferHubQueueParcelable : public Parcelable {
- public:
-  BufferHubQueueParcelable() = default;
-
-  BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default;
-  BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept {
-    channel_parcelable_ = std::move(other.channel_parcelable_);
-    return *this;
-  }
-
-  // Constructs an parcelable contains the channel parcelable.
-  explicit BufferHubQueueParcelable(
-      std::unique_ptr<pdx::ChannelParcelable> channel_parcelable)
-      : channel_parcelable_(std::move(channel_parcelable)) {}
-
-  BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete;
-  void operator=(const BufferHubQueueParcelable&) = delete;
-
-  bool IsValid() const;
-
-  // Returns a channel handle constructed from this parcelable object and takes
-  // the ownership of all resources from the parcelable object.
-  pdx::LocalChannelHandle TakeChannelHandle();
-
-  // Serializes the queue parcelable into the given parcel. Note that no system
-  // resources are getting duplicated, nor did the parcel takes ownership of the
-  // queue parcelable. Thus, the parcelable object must remain valid for the
-  // lifetime of the parcel.
-  status_t writeToParcel(Parcel* parcel) const override;
-
-  // Deserialize the queue parcelable from the given parcel. Note that system
-  // resources are duplicated from the parcel into the queue parcelable. Returns
-  // error if the targeting parcelable object is already valid.
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_;
-};
-
-using ProducerQueueParcelable =
-    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>;
-using ConsumerQueueParcelable =
-    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>;
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
deleted file mode 100644
index 2f14f7c..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-
-#include <android-base/unique_fd.h>
-#include <log/log.h>
-#include <sys/epoll.h>
-
-namespace android {
-namespace dvr {
-
-class EpollFileDescriptor {
- public:
-  static const int CTL_ADD = EPOLL_CTL_ADD;
-  static const int CTL_MOD = EPOLL_CTL_MOD;
-  static const int CTL_DEL = EPOLL_CTL_DEL;
-
-  EpollFileDescriptor() : fd_(-1) {}
-
-  // Constructs an EpollFileDescriptor from an integer file descriptor and
-  // takes ownership.
-  explicit EpollFileDescriptor(int fd) : fd_(fd) {}
-
-  bool IsValid() const { return fd_.get() >= 0; }
-
-  int Create() {
-    if (IsValid()) {
-      ALOGW("epoll fd has already been created.");
-      return -EALREADY;
-    }
-
-    fd_.reset(epoll_create1(EPOLL_CLOEXEC));
-
-    if (fd_.get() < 0)
-      return -errno;
-    else
-      return 0;
-  }
-
-  int Control(int op, int target_fd, epoll_event* ev) {
-    if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
-      return -errno;
-    else
-      return 0;
-  }
-
-  int Wait(epoll_event* events, int maxevents, int timeout) {
-    int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
-
-    if (ret < 0)
-      return -errno;
-    else
-      return ret;
-  }
-
-  int Get() const { return fd_.get(); }
-
- private:
-  base::unique_fd fd_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
deleted file mode 100644
index e373376..0000000
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-header_libraries = [
-    "libdvr_headers",
-]
-
-shared_libraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libgui",
-    "liblog",
-    "libhardware",
-    "libui",
-    "libutils",
-    "libnativewindow",
-    "libpdx_default_transport",
-]
-
-static_libraries = [
-    "libchrome",
-    "libdvrcommon",
-    "libperformance",
-]
-
-cc_test {
-    srcs: ["buffer_hub_queue-test.cpp"],
-    header_libs: header_libraries,
-    static_libs: static_libraries,
-    shared_libs: shared_libraries,
-    cflags: [
-        "-DLOG_TAG=\"buffer_hub_queue-test\"",
-        "-DTRACE=0",
-        "-O0",
-        "-g",
-        "-Wall",
-        "-Werror",
-        "-Wno-error=sign-compare", // to fix later
-    ],
-    name: "buffer_hub_queue-test",
-}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
deleted file mode 100644
index 6ae603b..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ /dev/null
@@ -1,1083 +0,0 @@
-#include <base/logging.h>
-#include <binder/Parcel.h>
-#include <dvr/dvr_api.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-
-#include <gtest/gtest.h>
-#include <poll.h>
-#include <sys/eventfd.h>
-
-#include <vector>
-
-// Enable/disable debug logging.
-#define TRACE 0
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalChannelHandle;
-using pdx::LocalHandle;
-
-namespace {
-
-constexpr uint32_t kBufferWidth = 100;
-constexpr uint32_t kBufferHeight = 1;
-constexpr uint32_t kBufferLayerCount = 1;
-constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
-constexpr int kTimeoutMs = 100;
-constexpr int kNoTimeout = 0;
-
-class BufferHubQueueTest : public ::testing::Test {
- public:
-  bool CreateProducerQueue(const ProducerQueueConfig& config,
-                           const UsagePolicy& usage) {
-    producer_queue_ = ProducerQueue::Create(config, usage);
-    return producer_queue_ != nullptr;
-  }
-
-  bool CreateConsumerQueue() {
-    if (producer_queue_) {
-      consumer_queue_ = producer_queue_->CreateConsumerQueue();
-      return consumer_queue_ != nullptr;
-    } else {
-      return false;
-    }
-  }
-
-  bool CreateQueues(const ProducerQueueConfig& config,
-                    const UsagePolicy& usage) {
-    return CreateProducerQueue(config, usage) && CreateConsumerQueue();
-  }
-
-  void AllocateBuffer(size_t* slot_out = nullptr) {
-    // Create producer buffer.
-    auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                                  kBufferLayerCount,
-                                                  kBufferFormat, kBufferUsage);
-
-    ASSERT_TRUE(status.ok());
-    size_t slot = status.take();
-    if (slot_out)
-      *slot_out = slot;
-  }
-
-  bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) {
-    pollfd pfd{queue->queue_fd(), POLLIN, 0};
-    int ret;
-    do {
-      ret = poll(&pfd, 1, timeout_ms);
-    } while (ret == -1 && errno == EINTR);
-
-    if (ret < 0) {
-      ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(),
-            strerror(errno));
-      return false;
-    } else if (ret == 0) {
-      return false;
-    }
-    return queue->HandleQueueEvents();
-  }
-
- protected:
-  ProducerQueueConfigBuilder config_builder_;
-  std::unique_ptr<ProducerQueue> producer_queue_;
-  std::unique_ptr<ConsumerQueue> consumer_queue_;
-};
-
-TEST_F(BufferHubQueueTest, TestDequeue) {
-  const int64_t nb_dequeue_times = 16;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate only one buffer.
-  AllocateBuffer();
-
-  // But dequeue multiple times.
-  for (int64_t i = 0; i < nb_dequeue_times; i++) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    // Producer gains a buffer.
-    auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-
-    // Consumer acquires a buffer.
-    auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-    auto c1 = c1_status.take();
-    ASSERT_NE(c1, nullptr);
-    EXPECT_EQ(mi.index, i);
-    EXPECT_EQ(mo.index, i);
-
-    // Consumer releases the buffer.
-    EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
-  }
-}
-
-TEST_F(BufferHubQueueTest,
-       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate 3 buffers to use.
-  const size_t test_queue_capacity = 3;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    AllocateBuffer();
-  }
-  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
-  size_t producer_slot, consumer_slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-
-  // Producer posts 2 buffers and remember their posted sequence.
-  std::deque<size_t> posted_slots;
-  for (int64_t i = 0; i < 2; i++) {
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer should not be gaining posted buffer when there are still
-    // available buffers to gain.
-    auto found_iter =
-        std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
-    EXPECT_EQ(found_iter, posted_slots.end());
-    posted_slots.push_back(producer_slot);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-  }
-
-  // Consumer acquires one buffer.
-  auto c1_status =
-      consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
-  EXPECT_TRUE(c1_status.ok());
-  auto c1 = c1_status.take();
-  ASSERT_NE(c1, nullptr);
-  // Consumer should get the oldest posted buffer. No checks here.
-  // posted_slots[0] should be in acquired state now.
-  EXPECT_EQ(mo.index, 0);
-  // Consumer releases the buffer.
-  EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
-  // posted_slots[0] should be in released state now.
-
-  // Producer gain and post 2 buffers.
-  for (int64_t i = 0; i < 2; i++) {
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // The gained buffer should be the one in released state or the one haven't
-    // been use.
-    EXPECT_NE(posted_slots[1], producer_slot);
-
-    mi.index = i + 2;
-    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-  }
-
-  // Producer gains a buffer.
-  auto p1_status =
-      producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
-  EXPECT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // The gained buffer should be the oldest posted buffer.
-  EXPECT_EQ(posted_slots[1], producer_slot);
-
-  // Producer posts the buffer.
-  mi.index = 4;
-  EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-}
-
-TEST_F(BufferHubQueueTest,
-       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate 4 buffers to use.
-  const size_t test_queue_capacity = 4;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    AllocateBuffer();
-  }
-  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
-  // Post all allowed buffers and remember their posted sequence.
-  std::deque<size_t> posted_slots;
-  for (int64_t i = 0; i < test_queue_capacity; i++) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    // Producer gains a buffer.
-    auto p1_status =
-        producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // Producer should not be gaining posted buffer when there are still
-    // available buffers to gain.
-    auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
-    EXPECT_EQ(found_iter, posted_slots.end());
-    posted_slots.push_back(slot);
-
-    // Producer posts the buffer.
-    mi.index = i;
-    EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-  }
-
-  // Gain posted buffers in sequence.
-  const int64_t nb_dequeue_all_times = 2;
-  for (int j = 0; j < nb_dequeue_all_times; ++j) {
-    for (int i = 0; i < test_queue_capacity; ++i) {
-      size_t slot;
-      LocalHandle fence;
-      DvrNativeBufferMetadata mi, mo;
-
-      // Producer gains a buffer.
-      auto p1_status =
-          producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
-      EXPECT_TRUE(p1_status.ok());
-      auto p1 = p1_status.take();
-      ASSERT_NE(p1, nullptr);
-
-      // The gained buffer should be the oldest posted buffer.
-      EXPECT_EQ(posted_slots[i], slot);
-
-      // Producer posts the buffer.
-      mi.index = i + test_queue_capacity * (j + 1);
-      EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-    }
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestProducerConsumer) {
-  const size_t kBufferCount = 16;
-  size_t slot;
-  DvrNativeBufferMetadata mi, mo;
-  LocalHandle fence;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-
-    // Producer queue has all the available buffers on initialize.
-    ASSERT_EQ(producer_queue_->count(), i + 1);
-    ASSERT_EQ(producer_queue_->capacity(), i + 1);
-
-    // Consumer queue has no avaiable buffer on initialize.
-    ASSERT_EQ(consumer_queue_->count(), 0U);
-    // Consumer queue does not import buffers until a dequeue is issued.
-    ASSERT_EQ(consumer_queue_->capacity(), i);
-    // Dequeue returns timeout since no buffer is ready to consumer, but
-    // this implicitly triggers buffer import and bump up |capacity|.
-    auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
-    ASSERT_FALSE(status.ok());
-    ASSERT_EQ(ETIMEDOUT, status.error());
-    ASSERT_EQ(consumer_queue_->capacity(), i + 1);
-  }
-
-  // Use eventfd as a stand-in for a fence.
-  LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    // First time there is no buffer available to dequeue.
-    auto consumer_status =
-        consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
-    ASSERT_FALSE(consumer_status.ok());
-    ASSERT_EQ(consumer_status.error(), ETIMEDOUT);
-
-    // Make sure Producer buffer is POSTED so that it's ready to Accquire
-    // in the consumer's Dequeue() function.
-    auto producer_status =
-        producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    auto producer = producer_status.take();
-    ASSERT_NE(nullptr, producer);
-
-    mi.index = static_cast<int64_t>(i);
-    ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0);
-
-    // Second time the just the POSTED buffer should be dequeued.
-    consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok());
-    EXPECT_TRUE(fence.IsValid());
-
-    auto consumer = consumer_status.take();
-    ASSERT_NE(nullptr, consumer);
-    ASSERT_EQ(mi.index, mo.index);
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestInsertBuffer) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  consumer_queue_ = producer_queue_->CreateConsumerQueue();
-  ASSERT_TRUE(consumer_queue_ != nullptr);
-  EXPECT_EQ(producer_queue_->capacity(), 0);
-  EXPECT_EQ(consumer_queue_->capacity(), 0);
-
-  std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
-      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
-  ASSERT_TRUE(p1 != nullptr);
-  ASSERT_EQ(p1->GainAsync(), 0);
-
-  // Inserting a posted buffer will fail.
-  DvrNativeBufferMetadata meta;
-  EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
-  auto status_or_slot = producer_queue_->InsertBuffer(p1);
-  EXPECT_FALSE(status_or_slot.ok());
-  EXPECT_EQ(status_or_slot.error(), EINVAL);
-
-  // Inserting a gained buffer will succeed.
-  std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
-      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
-  ASSERT_EQ(p2->GainAsync(), 0);
-  ASSERT_TRUE(p2 != nullptr);
-  status_or_slot = producer_queue_->InsertBuffer(p2);
-  EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
-  // This is the first buffer inserted, should take slot 0.
-  size_t slot = status_or_slot.get();
-  EXPECT_EQ(slot, 0);
-
-  // Wait and expect the consumer to kick up the newly inserted buffer.
-  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
-}
-
-TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-  DvrNativeBufferMetadata mo;
-
-  // Allocate buffers.
-  const size_t kBufferCount = 4u;
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-  }
-  ASSERT_EQ(kBufferCount, producer_queue_->count());
-  ASSERT_EQ(kBufferCount, producer_queue_->capacity());
-
-  consumer_queue_ = producer_queue_->CreateConsumerQueue();
-  ASSERT_NE(nullptr, consumer_queue_);
-
-  // Check that buffers are correctly imported on construction.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_EQ(0u, consumer_queue_->count());
-
-  // Dequeue all the buffers and keep track of them in an array. This prevents
-  // the producer queue ring buffer ref counts from interfering with the tests.
-  struct Entry {
-    std::shared_ptr<ProducerBuffer> buffer;
-    LocalHandle fence;
-    size_t slot;
-  };
-  std::array<Entry, kBufferCount> buffers;
-
-  for (size_t i = 0; i < kBufferCount; i++) {
-    Entry* entry = &buffers[i];
-    auto producer_status =
-        producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
-    ASSERT_TRUE(producer_status.ok());
-    entry->buffer = producer_status.take();
-    ASSERT_NE(nullptr, entry->buffer);
-  }
-
-  // Remove a buffer and make sure both queues reflect the change.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
-  // As long as the removed buffer is still alive the consumer queue won't know
-  // its gone.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Release the removed buffer.
-  buffers[0].buffer = nullptr;
-
-  // Now the consumer queue should know it's gone.
-  EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs));
-  ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-
-  // Allocate a new buffer. This should take the first empty slot.
-  size_t slot;
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[0].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  // The consumer queue should pick up the new buffer.
-  EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Remove and allocate a buffer.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-  buffers[1].buffer = nullptr;
-
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[1].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  // The consumer queue should pick up the new buffer but the count shouldn't
-  // change.
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Remove and allocate a buffer, but don't free the buffer right away.
-  ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot));
-  EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
-  AllocateBuffer(&slot);
-  ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
-  EXPECT_EQ(buffers[2].slot, slot);
-  EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
-  // Release the producer buffer to trigger a POLLHUP event for an already
-  // removed buffer.
-  buffers[2].buffer = nullptr;
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-  EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
-  EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-}
-
-TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
-  // ProducerConfigureBuilder doesn't set Metadata{size}, which means there
-  // is no metadata associated with this BufferQueue's buffer.
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  // Allocate buffers.
-  const size_t kBufferCount = 4u;
-  for (size_t i = 0; i < kBufferCount; i++) {
-    AllocateBuffer();
-  }
-  ASSERT_EQ(kBufferCount, producer_queue_->count());
-
-  // Build a silent consumer queue to test multi-consumer queue features.
-  auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
-  ASSERT_NE(nullptr, silent_queue);
-
-  // Check that silent queue doesn't import buffers on creation.
-  EXPECT_EQ(silent_queue->capacity(), 0U);
-
-  // Dequeue and post a buffer.
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto producer_status =
-      producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(producer_status.ok());
-  auto producer_buffer = producer_status.take();
-  ASSERT_NE(producer_buffer, nullptr);
-  EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-  // After post, check the number of remaining available buffers.
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  // Currently we expect no buffer to be available prior to calling
-  // WaitForBuffers/HandleQueueEvents.
-  // TODO(eieio): Note this behavior may change in the future.
-  EXPECT_EQ(silent_queue->count(), 0U);
-  EXPECT_FALSE(silent_queue->HandleQueueEvents());
-  EXPECT_EQ(silent_queue->count(), 0U);
-
-  // Build a new consumer queue to test multi-consumer queue features.
-  consumer_queue_ = silent_queue->CreateConsumerQueue();
-  ASSERT_NE(consumer_queue_, nullptr);
-
-  // Check that buffers are correctly imported on construction.
-  EXPECT_EQ(consumer_queue_->capacity(), kBufferCount);
-  // Buffers are only imported, but their availability is not checked until
-  // first call to Dequeue().
-  EXPECT_EQ(consumer_queue_->count(), 0U);
-
-  // Reclaim released/ignored buffers.
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  usleep(10000);
-  WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
-  // Post another buffer.
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(producer_status.ok());
-  producer_buffer = producer_status.take();
-  ASSERT_NE(producer_buffer, nullptr);
-  EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-
-  // Verify that the consumer queue receives it.
-  size_t consumer_queue_count = consumer_queue_->count();
-  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
-  EXPECT_GT(consumer_queue_->count(), consumer_queue_count);
-
-  // Save the current consumer queue buffer count to compare after the dequeue.
-  consumer_queue_count = consumer_queue_->count();
-
-  // Dequeue and acquire/release (discard) buffers on the consumer end.
-  auto consumer_status =
-      consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(consumer_status.ok());
-  auto consumer_buffer = consumer_status.take();
-  ASSERT_NE(consumer_buffer, nullptr);
-  consumer_buffer->Discard();
-
-  // Buffer should be returned to the producer queue without being handled by
-  // the silent consumer queue.
-  EXPECT_LT(consumer_queue_->count(), consumer_queue_count);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 2);
-
-  WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
-  EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-}
-
-struct TestUserMetadata {
-  char a;
-  int32_t b;
-  int64_t c;
-};
-
-constexpr uint64_t kUserMetadataSize =
-    static_cast<uint64_t>(sizeof(TestUserMetadata));
-
-TEST_F(BufferHubQueueTest, TestUserMetadata) {
-  ASSERT_TRUE(CreateQueues(
-      config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
-  AllocateBuffer();
-
-  std::vector<TestUserMetadata> user_metadata_list = {
-      {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
-
-  for (auto user_metadata : user_metadata_list) {
-    size_t slot;
-    LocalHandle fence;
-    DvrNativeBufferMetadata mi, mo;
-
-    auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(p1_status.ok());
-    auto p1 = p1_status.take();
-    ASSERT_NE(p1, nullptr);
-
-    // TODO(b/69469185): Test against metadata from consumer once we implement
-    // release metadata properly.
-    // EXPECT_EQ(mo.user_metadata_ptr, 0U);
-    // EXPECT_EQ(mo.user_metadata_size, 0U);
-
-    mi.user_metadata_size = kUserMetadataSize;
-    mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-    EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-    auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-    auto c1 = c1_status.take();
-    ASSERT_NE(c1, nullptr);
-
-    EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize);
-    auto out_user_metadata =
-        reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr);
-    EXPECT_EQ(user_metadata.a, out_user_metadata->a);
-    EXPECT_EQ(user_metadata.b, out_user_metadata->b);
-    EXPECT_EQ(user_metadata.c, out_user_metadata->c);
-
-    // When release, empty metadata is also legit.
-    mi.user_metadata_size = 0U;
-    mi.user_metadata_ptr = 0U;
-    c1->ReleaseAsync(&mi, {});
-  }
-}
-
-TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) {
-  ASSERT_TRUE(CreateQueues(
-      config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
-  AllocateBuffer();
-
-  TestUserMetadata user_metadata;
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  EXPECT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // Post with mismatched user metadata size will fail. But the producer buffer
-  // itself should stay untouched.
-  mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-  mi.user_metadata_size = kUserMetadataSize + 1;
-  EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG);
-  // Post with the exact same user metdata size can success.
-  mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
-  mi.user_metadata_size = kUserMetadataSize;
-  EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestEnqueue) {
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{}));
-  AllocateBuffer();
-
-  size_t slot;
-  LocalHandle fence;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(nullptr, p1);
-
-  producer_queue_->Enqueue(p1, slot, 0ULL);
-  auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_FALSE(c1_status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  size_t ps1;
-  AllocateBuffer();
-  LocalHandle fence;
-  DvrNativeBufferMetadata mi, mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_NE(p1, nullptr);
-
-  // producer queue is exhausted
-  size_t ps2;
-  auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
-  ASSERT_FALSE(p2_status.ok());
-  ASSERT_EQ(ETIMEDOUT, p2_status.error());
-
-  // dynamically add buffer.
-  AllocateBuffer();
-  ASSERT_EQ(producer_queue_->count(), 1U);
-  ASSERT_EQ(producer_queue_->capacity(), 2U);
-
-  // now we can dequeue again
-  p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
-  ASSERT_TRUE(p2_status.ok());
-  auto p2 = p2_status.take();
-  ASSERT_NE(p2, nullptr);
-  ASSERT_EQ(producer_queue_->count(), 0U);
-  // p1 and p2 should have different slot number
-  ASSERT_NE(ps1, ps2);
-
-  // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
-  // are called. So far consumer_queue_ should be empty.
-  ASSERT_EQ(consumer_queue_->count(), 0U);
-
-  int64_t seq = 1;
-  mi.index = seq;
-  ASSERT_EQ(p1->PostAsync(&mi, {}), 0);
-
-  size_t cs1, cs2;
-  auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
-  ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
-  auto c1 = c1_status.take();
-  ASSERT_NE(c1, nullptr);
-  ASSERT_EQ(consumer_queue_->count(), 0U);
-  ASSERT_EQ(consumer_queue_->capacity(), 2U);
-  ASSERT_EQ(cs1, ps1);
-
-  ASSERT_EQ(p2->PostAsync(&mi, {}), 0);
-  auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence);
-  ASSERT_TRUE(c2_status.ok());
-  auto c2 = c2_status.take();
-  ASSERT_NE(c2, nullptr);
-  ASSERT_EQ(cs2, ps2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-  auto status = producer_queue_->AllocateBuffers(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage, /*buffer_count=*/2);
-  ASSERT_TRUE(status.ok());
-  std::vector<size_t> buffer_slots = status.take();
-  ASSERT_EQ(buffer_slots.size(), 2);
-  ASSERT_EQ(producer_queue_->capacity(), 2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-  auto status = producer_queue_->AllocateBuffers(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage, /*buffer_count=*/0);
-  ASSERT_TRUE(status.ok());
-  std::vector<size_t> buffer_slots = status.take();
-  ASSERT_EQ(buffer_slots.size(), 0);
-  ASSERT_EQ(producer_queue_->capacity(), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageSetMask) {
-  const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(
-      CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0}));
-
-  // When allocation, leave out |set_mask| from usage bits on purpose.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~set_mask);
-  ASSERT_TRUE(status.ok());
-
-  LocalHandle fence;
-  size_t slot;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_EQ(p1->usage() & set_mask, set_mask);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageClearMask) {
-  const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(
-      CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0}));
-
-  // When allocation, add |clear_mask| into usage bits on purpose.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | clear_mask);
-  ASSERT_TRUE(status.ok());
-
-  LocalHandle fence;
-  size_t slot;
-  DvrNativeBufferMetadata mo;
-  auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(p1_status.ok());
-  auto p1 = p1_status.take();
-  ASSERT_EQ(p1->usage() & clear_mask, 0U);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
-  const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{0, 0, deny_set_mask, 0}));
-
-  // Now that |deny_set_mask| is illegal, allocation without those bits should
-  // be able to succeed.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~deny_set_mask);
-  ASSERT_TRUE(status.ok());
-
-  // While allocation with those bits should fail.
-  status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                           kBufferLayerCount, kBufferFormat,
-                                           kBufferUsage | deny_set_mask);
-  ASSERT_FALSE(status.ok());
-  ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
-  const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
-                           UsagePolicy{0, 0, 0, deny_clear_mask}));
-
-  // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
-  // mandatory), allocation with those bits should be able to succeed.
-  auto status = producer_queue_->AllocateBuffer(
-      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | deny_clear_mask);
-  ASSERT_TRUE(status.ok());
-
-  // While allocation without those bits should fail.
-  status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                           kBufferLayerCount, kBufferFormat,
-                                           kBufferUsage & ~deny_clear_mask);
-  ASSERT_FALSE(status.ok());
-  ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestQueueInfo) {
-  static const bool kIsAsync = true;
-  ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync)
-                               .SetDefaultWidth(kBufferWidth)
-                               .SetDefaultHeight(kBufferHeight)
-                               .SetDefaultFormat(kBufferFormat)
-                               .Build(),
-                           UsagePolicy{}));
-
-  EXPECT_EQ(producer_queue_->default_width(), kBufferWidth);
-  EXPECT_EQ(producer_queue_->default_height(), kBufferHeight);
-  EXPECT_EQ(producer_queue_->default_format(), kBufferFormat);
-  EXPECT_EQ(producer_queue_->is_async(), kIsAsync);
-
-  EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth);
-  EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight);
-  EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat);
-  EXPECT_EQ(consumer_queue_->is_async(), kIsAsync);
-}
-
-TEST_F(BufferHubQueueTest, TestFreeAllBuffers) {
-  constexpr size_t kBufferCount = 2;
-
-#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers)  \
-  EXPECT_EQ(consumer_queue_->count(), 0U);          \
-  EXPECT_EQ(consumer_queue_->capacity(), 0U);       \
-  EXPECT_EQ(producer_queue_->count(), 0U);          \
-  EXPECT_EQ(producer_queue_->capacity(), 0U);       \
-  for (size_t i = 0; i < num_buffers; i++) {        \
-    AllocateBuffer();                               \
-  }                                                 \
-  EXPECT_EQ(producer_queue_->count(), num_buffers); \
-  EXPECT_EQ(producer_queue_->capacity(), num_buffers);
-
-  size_t slot;
-  LocalHandle fence;
-  pdx::Status<void> status;
-  pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
-  pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
-  std::shared_ptr<ConsumerBuffer> consumer_buffer;
-  std::shared_ptr<ProducerBuffer> producer_buffer;
-  DvrNativeBufferMetadata mi, mo;
-
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  // Free all buffers when buffers are avaible for dequeue.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when one buffer is dequeued.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(producer_status.ok());
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are dequeued.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-  }
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when one buffer is posted.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-  ASSERT_TRUE(producer_status.ok());
-  producer_buffer = producer_status.take();
-  ASSERT_NE(nullptr, producer_buffer);
-  ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence));
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are posted.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    producer_buffer = producer_status.take();
-    ASSERT_NE(producer_buffer, nullptr);
-    ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
-  }
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // Free all buffers when all buffers are acquired.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-  for (size_t i = 0; i < kBufferCount; i++) {
-    producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(producer_status.ok());
-    producer_buffer = producer_status.take();
-    ASSERT_NE(producer_buffer, nullptr);
-    ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
-    consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
-  }
-
-  status = producer_queue_->FreeAllBuffers();
-  EXPECT_TRUE(status.ok());
-
-  // In addition to FreeAllBuffers() from the queue, it is also required to
-  // delete all references to the ProducerBuffer (i.e. the PDX client).
-  producer_buffer = nullptr;
-
-  // Crank consumer queue events to pickup EPOLLHUP events on the queue.
-  consumer_queue_->HandleQueueEvents();
-
-  // One last check.
-  CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-
-#undef CHECK_NO_BUFFER_THEN_ALLOCATE
-}
-
-TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) {
-  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
-                           UsagePolicy{}));
-
-  // Allocate only one buffer.
-  AllocateBuffer();
-
-  // Export should fail as the queue is not empty.
-  auto status = producer_queue_->TakeAsParcelable();
-  EXPECT_FALSE(status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) {
-  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
-  auto s1 = producer_queue_->TakeAsParcelable();
-  EXPECT_TRUE(s1.ok());
-
-  ProducerQueueParcelable output_parcelable = s1.take();
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  Parcel parcel;
-  status_t res;
-  res = output_parcelable.writeToParcel(&parcel);
-  EXPECT_EQ(res, OK);
-
-  // After written into parcelable, the output_parcelable is still valid has
-  // keeps the producer channel alive.
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  // Creating producer buffer should fail.
-  auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
-                                            kBufferLayerCount, kBufferFormat,
-                                            kBufferUsage);
-  ASSERT_FALSE(s2.ok());
-
-  // Reset the data position so that we can read back from the same parcel
-  // without doing actually Binder IPC.
-  parcel.setDataPosition(0);
-  producer_queue_ = nullptr;
-
-  // Recreate the producer queue from the parcel.
-  ProducerQueueParcelable input_parcelable;
-  EXPECT_FALSE(input_parcelable.IsValid());
-
-  res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, OK);
-  EXPECT_TRUE(input_parcelable.IsValid());
-
-  EXPECT_EQ(producer_queue_, nullptr);
-  producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle());
-  EXPECT_FALSE(input_parcelable.IsValid());
-  ASSERT_NE(producer_queue_, nullptr);
-
-  // Newly created queue from the parcel can allocate buffer, post buffer to
-  // consumer.
-  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
-  EXPECT_EQ(producer_queue_->count(), 1U);
-  EXPECT_EQ(producer_queue_->capacity(), 1U);
-
-  size_t slot;
-  DvrNativeBufferMetadata producer_meta;
-  DvrNativeBufferMetadata consumer_meta;
-  LocalHandle fence;
-  auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
-  EXPECT_TRUE(s3.ok());
-
-  std::shared_ptr<ProducerBuffer> p1 = s3.take();
-  ASSERT_NE(p1, nullptr);
-
-  producer_meta.timestamp = 42;
-  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
-  // Make sure the buffer can be dequeued from consumer side.
-  auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
-  EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
-  auto consumer = s4.take();
-  ASSERT_NE(consumer, nullptr);
-  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) {
-  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
-  auto s1 = producer_queue_->CreateConsumerQueueParcelable();
-  EXPECT_TRUE(s1.ok());
-  ConsumerQueueParcelable output_parcelable = s1.take();
-  EXPECT_TRUE(output_parcelable.IsValid());
-
-  // Write to a Parcel new object.
-  Parcel parcel;
-  status_t res;
-  res = output_parcelable.writeToParcel(&parcel);
-
-  // Reset the data position so that we can read back from the same parcel
-  // without doing actually Binder IPC.
-  parcel.setDataPosition(0);
-
-  // No consumer queue created yet.
-  EXPECT_EQ(consumer_queue_, nullptr);
-
-  // If the parcel contains a consumer queue, read into a
-  // ProducerQueueParcelable should fail.
-  ProducerQueueParcelable wrongly_typed_parcelable;
-  EXPECT_FALSE(wrongly_typed_parcelable.IsValid());
-  res = wrongly_typed_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, -EINVAL);
-  parcel.setDataPosition(0);
-
-  // Create the consumer queue from the parcel.
-  ConsumerQueueParcelable input_parcelable;
-  EXPECT_FALSE(input_parcelable.IsValid());
-
-  res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, OK);
-  EXPECT_TRUE(input_parcelable.IsValid());
-
-  consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
-  EXPECT_FALSE(input_parcelable.IsValid());
-  ASSERT_NE(consumer_queue_, nullptr);
-
-  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
-  EXPECT_EQ(producer_queue_->count(), 1U);
-  EXPECT_EQ(producer_queue_->capacity(), 1U);
-
-  size_t slot;
-  DvrNativeBufferMetadata producer_meta;
-  DvrNativeBufferMetadata consumer_meta;
-  LocalHandle fence;
-  auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
-  EXPECT_TRUE(s2.ok());
-
-  std::shared_ptr<ProducerBuffer> p1 = s2.take();
-  ASSERT_NE(p1, nullptr);
-
-  producer_meta.timestamp = 42;
-  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
-  // Make sure the buffer can be dequeued from consumer side.
-  auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
-  EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
-  auto consumer = s3.take();
-  ASSERT_NE(consumer, nullptr);
-  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-}  // namespace
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
deleted file mode 100644
index b0ed950..0000000
--- a/libs/vr/libdisplay/Android.bp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "display_client.cpp",
-    "display_manager_client.cpp",
-    "display_protocol.cpp",
-    "shared_buffer_helpers.cpp",
-    "vsync_service.cpp",
-]
-
-localIncludeFiles = [
-    "include",
-]
-
-sharedLibraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "liblog",
-    "libutils",
-    "libui",
-    "libgui",
-    "libhardware",
-    "libsync",
-    "libnativewindow",
-    "libpdx_default_transport",
-]
-
-staticLibraries = [
-    "libdvrcommon",
-    "libbroadcastring",
-]
-
-headerLibraries = [
-    "vulkan_headers",
-    "libdvr_headers",
-]
-
-cc_library {
-    srcs: sourceFiles,
-    cflags: ["-DLOG_TAG=\"libdisplay\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-DGL_GLEXT_PROTOTYPES",
-        "-DEGL_EGLEXT_PROTOTYPES",
-        "-Wall",
-        "-Werror",
-    ],  // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
-    export_include_dirs: localIncludeFiles,
-    shared_libs: sharedLibraries,
-    static_libs: staticLibraries,
-    header_libs: headerLibraries,
-    export_header_lib_headers: headerLibraries,
-
-    name: "libdisplay",
-}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
deleted file mode 100644
index 62856df..0000000
--- a/libs/vr/libdisplay/display_client.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "include/private/dvr/display_client.h"
-
-#include <cutils/native_handle.h>
-#include <log/log.h>
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/status.h>
-
-#include <mutex>
-
-#include <private/dvr/display_protocol.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-Surface::Surface(LocalChannelHandle channel_handle, int* error)
-    : BASE{pdx::default_transport::ClientChannel::Create(
-          std::move(channel_handle))} {
-  auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
-  if (!status) {
-    ALOGE("Surface::Surface: Failed to get surface info: %s",
-          status.GetErrorMessage().c_str());
-    Close(status.error());
-    if (error)
-      *error = status.error();
-  }
-
-  surface_id_ = status.get().surface_id;
-  z_order_ = status.get().z_order;
-  visible_ = status.get().visible;
-}
-
-Surface::Surface(const SurfaceAttributes& attributes, int* error)
-    : BASE{pdx::default_transport::ClientChannelFactory::Create(
-               DisplayProtocol::kClientPath),
-           kInfiniteTimeout} {
-  auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
-  if (!status) {
-    ALOGE("Surface::Surface: Failed to create display surface: %s",
-          status.GetErrorMessage().c_str());
-    Close(status.error());
-    if (error)
-      *error = status.error();
-  }
-
-  surface_id_ = status.get().surface_id;
-  z_order_ = status.get().z_order;
-  visible_ = status.get().visible;
-}
-
-Status<void> Surface::SetVisible(bool visible) {
-  return SetAttributes(
-      {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
-}
-
-Status<void> Surface::SetZOrder(int z_order) {
-  return SetAttributes(
-      {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
-}
-
-Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
-  if (!status) {
-    ALOGE(
-        "Surface::SetAttributes: Failed to set display surface "
-        "attributes: %s",
-        status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  // Set the local cached copies of the attributes we care about from the full
-  // set of attributes sent to the display service.
-  for (const auto& attribute : attributes) {
-    const auto& key = attribute.first;
-    const auto* variant = &attribute.second;
-    bool invalid_value = false;
-    switch (key) {
-      case SurfaceAttribute::Visible:
-        invalid_value =
-            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
-        break;
-      case SurfaceAttribute::ZOrder:
-        invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
-        break;
-    }
-
-    if (invalid_value) {
-      ALOGW(
-          "Surface::SetAttributes: Failed to set display surface "
-          "attribute %d because of incompatible type: %d",
-          key, variant->index());
-    }
-  }
-
-  return {};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
-    uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) {
-  ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
-  auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(
-      ProducerQueueConfigBuilder()
-          .SetDefaultWidth(width)
-          .SetDefaultHeight(height)
-          .SetDefaultFormat(format)
-          .SetMetadataSize(metadata_size)
-          .Build());
-  if (!status) {
-    ALOGE("Surface::CreateQueue: Failed to create queue: %s",
-          status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto producer_queue = ProducerQueue::Import(status.take());
-  if (!producer_queue) {
-    ALOGE("Surface::CreateQueue: Failed to import producer queue!");
-    return ErrorStatus(ENOMEM);
-  }
-
-  return {std::move(producer_queue)};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
-    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
-    uint64_t usage, size_t capacity, size_t metadata_size) {
-  ALOGD_IF(TRACE,
-           "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
-           "usage=%" PRIx64 " capacity=%zu",
-           width, height, layer_count, format, usage, capacity);
-  auto status = CreateQueue(width, height, format, metadata_size);
-  if (!status)
-    return status.error_status();
-
-  auto producer_queue = status.take();
-
-  ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
-  auto allocate_status = producer_queue->AllocateBuffers(
-      width, height, layer_count, format, usage, capacity);
-  if (!allocate_status) {
-    ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
-          producer_queue->id(), allocate_status.GetErrorMessage().c_str());
-    return allocate_status.error_status();
-  }
-
-  return {std::move(producer_queue)};
-}
-
-DisplayClient::DisplayClient(int* error)
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-               DisplayProtocol::kClientPath),
-           kInfiniteTimeout) {
-  if (error)
-    *error = Client::error();
-}
-
-Status<Metrics> DisplayClient::GetDisplayMetrics() {
-  return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
-}
-
-Status<std::string> DisplayClient::GetConfigurationData(
-    ConfigFileType config_type) {
-  auto status =
-      InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type);
-  if (!status && status.error() != ENOENT) {
-    ALOGE(
-        "DisplayClient::GetConfigurationData: Unable to get"
-        "configuration data. Error: %s",
-        status.GetErrorMessage().c_str());
-  }
-  return status;
-}
-
-Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
-  return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
-}
-
-Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
-    const SurfaceAttributes& attributes) {
-  int error;
-  if (auto client = Surface::Create(attributes, &error))
-    return {std::move(client)};
-  else
-    return ErrorStatus(error);
-}
-
-pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer(
-    DvrGlobalBufferKey key, size_t size, uint64_t usage) {
-  auto status =
-      InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage);
-  if (!status) {
-    ALOGE(
-        "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer "
-        "%s",
-        status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto ion_buffer = std::make_unique<IonBuffer>();
-  auto native_buffer_handle = status.take();
-  const int ret = native_buffer_handle.Import(ion_buffer.get());
-  if (ret < 0) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
-        "key=%d; error=%s",
-        key, strerror(-ret));
-    return ErrorStatus(-ret);
-  }
-
-  return {std::move(ion_buffer)};
-}
-
-pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key);
-  if (!status) {
-    ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s",
-          status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer(
-    DvrGlobalBufferKey key) {
-  auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key);
-  if (!status) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; "
-        "error=%s",
-        key, status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  auto ion_buffer = std::make_unique<IonBuffer>();
-  auto native_buffer_handle = status.take();
-  const int ret = native_buffer_handle.Import(ion_buffer.get());
-  if (ret < 0) {
-    ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
-        "key=%d; error=%s",
-        key, strerror(-ret));
-    return ErrorStatus(-ret);
-  }
-
-  return {std::move(ion_buffer)};
-}
-
-Status<bool> DisplayClient::IsVrAppRunning() {
-  return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
-}
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
deleted file mode 100644
index fdeeb70..0000000
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "include/private/dvr/display_manager_client.h"
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <utils/Log.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-DisplayManagerClient::DisplayManagerClient()
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-          DisplayManagerProtocol::kClientPath)) {}
-
-DisplayManagerClient::~DisplayManagerClient() {}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerClient::GetSurfaceState() {
-  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
-        status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-pdx::Status<std::unique_ptr<ConsumerQueue>>
-DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
-  auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
-      surface_id, queue_id);
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
-        "surface_id=%d queue_id=%d: %s",
-        surface_id, queue_id, status.GetErrorMessage().c_str());
-    return status.error_status();
-  }
-
-  return {ConsumerQueue::Import(status.take())};
-}
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
deleted file mode 100644
index 773f9a5..0000000
--- a/libs/vr/libdisplay/display_protocol.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "include/private/dvr/display_protocol.h"
-
-namespace android {
-namespace dvr {
-namespace display {
-
-constexpr char DisplayProtocol::kClientPath[];
-constexpr char DisplayManagerProtocol::kClientPath[];
-constexpr char VSyncProtocol::kClientPath[];
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
deleted file mode 100644
index 81546ac..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_CLIENT_H_
-
-#include <dvr/dvr_api.h>
-#include <hardware/hwcomposer.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-namespace display {
-
-class Surface : public pdx::ClientBase<Surface> {
- public:
-  // Utility named constructor. This can be removed once ClientBase::Create is
-  // refactored to return Status<T> types.
-  static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
-      const SurfaceAttributes& attributes) {
-    int error;
-    pdx::Status<std::unique_ptr<Surface>> status;
-    if (auto surface = Create(attributes, &error))
-      status.SetValue(std::move(surface));
-    else
-      status.SetError(error);
-    return status;
-  }
-
-  int surface_id() const { return surface_id_; }
-  int z_order() const { return z_order_; }
-  bool visible() const { return visible_; }
-
-  pdx::Status<void> SetVisible(bool visible);
-  pdx::Status<void> SetZOrder(int z_order);
-  pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
-
-  // Creates an empty queue.
-  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
-                                                          uint32_t height,
-                                                          uint32_t format,
-                                                          size_t metadata_size);
-
-  // Creates a queue and populates it with |capacity| buffers of the specified
-  // parameters.
-  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
-                                                          uint32_t height,
-                                                          uint32_t layer_count,
-                                                          uint32_t format,
-                                                          uint64_t usage,
-                                                          size_t capacity,
-                                                          size_t metadata_size);
-
- private:
-  friend BASE;
-
-  int surface_id_ = -1;
-  int z_order_ = 0;
-  bool visible_ = false;
-
-  // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
-  // returns Status<T>.
-  explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
-  explicit Surface(pdx::LocalChannelHandle channel_handle,
-                   int* error = nullptr);
-
-  Surface(const Surface&) = delete;
-  void operator=(const Surface&) = delete;
-};
-
-class DisplayClient : public pdx::ClientBase<DisplayClient> {
- public:
-  pdx::Status<Metrics> GetDisplayMetrics();
-  pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
-  pdx::Status<uint8_t> GetDisplayIdentificationPort();
-  pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
-      DvrGlobalBufferKey key, size_t size, uint64_t usage);
-  pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
-  pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer(
-      DvrGlobalBufferKey key);
-  pdx::Status<std::unique_ptr<Surface>> CreateSurface(
-      const SurfaceAttributes& attributes);
-
-  // Temporary query for current VR status. Will be removed later.
-  pdx::Status<bool> IsVrAppRunning();
-
- private:
-  friend BASE;
-
-  explicit DisplayClient(int* error = nullptr);
-
-  DisplayClient(const DisplayClient&) = delete;
-  void operator=(const DisplayClient&) = delete;
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
deleted file mode 100644
index 45aef51..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-class ConsumerQueue;
-
-namespace display {
-
-class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
- public:
-  ~DisplayManagerClient() override;
-
-  pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
-  pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
-                                                              int queue_id);
-
-  using Client::event_fd;
-
-  pdx::Status<int> GetEventMask(int events) {
-    if (auto* client_channel = GetChannel())
-      return client_channel->GetEventMask(events);
-    else
-      return pdx::ErrorStatus(EINVAL);
-  }
-
- private:
-  friend BASE;
-
-  DisplayManagerClient();
-
-  DisplayManagerClient(const DisplayManagerClient&) = delete;
-  void operator=(const DisplayManagerClient&) = delete;
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
deleted file mode 100644
index 9f4cc4a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ /dev/null
@@ -1,304 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
-#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
-
-#include <sys/types.h>
-
-#include <array>
-#include <map>
-
-#include <dvr/dvr_display_types.h>
-
-#include <dvr/dvr_api.h>
-#include <pdx/rpc/buffer_wrapper.h>
-#include <pdx/rpc/remote_method.h>
-#include <pdx/rpc/serializable.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/bufferhub_rpc.h>
-
-// RPC protocol definitions for DVR display services (VrFlinger).
-
-namespace android {
-namespace dvr {
-namespace display {
-
-// Native display metrics.
-struct Metrics {
-  // Basic display properties.
-  uint32_t display_width;
-  uint32_t display_height;
-  uint32_t display_x_dpi;
-  uint32_t display_y_dpi;
-  uint32_t vsync_period_ns;
-
-  // HMD metrics.
-  // TODO(eieio): Determine how these fields should be populated. On phones
-  // these values are determined at runtime by VrCore based on which headset the
-  // phone is in. On dedicated hardware this needs to come from somewhere else.
-  // Perhaps these should be moved to a separate structure that is returned by a
-  // separate runtime call.
-  uint32_t distorted_width;
-  uint32_t distorted_height;
-  uint32_t hmd_ipd_mm;
-  float inter_lens_distance_m;
-  std::array<float, 4> left_fov_lrbt;
-  std::array<float, 4> right_fov_lrbt;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
-                           display_x_dpi, display_y_dpi, vsync_period_ns,
-                           distorted_width, distorted_height, hmd_ipd_mm,
-                           inter_lens_distance_m, left_fov_lrbt,
-                           right_fov_lrbt);
-};
-
-// Serializable base type for enum structs. Enum structs are easier to use than
-// enum classes, especially for bitmasks. This base type provides common
-// utilities for flags types.
-template <typename Integer>
-class Flags {
- public:
-  using Base = Flags<Integer>;
-  using Type = Integer;
-
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  Flags(const Integer& value) : value_{value} {}
-  Flags(const Flags&) = default;
-  Flags& operator=(const Flags&) = default;
-
-  Integer value() const { return value_; }
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator Integer() const { return value_; }
-
-  bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
-  bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
-
-  void Set(Integer bits) { value_ |= bits; }
-  void Clear(Integer bits) { value_ &= ~bits; }
-
-  Integer operator|(Integer bits) const { return value_ | bits; }
-  Integer operator&(Integer bits) const { return value_ & bits; }
-
-  Flags& operator|=(Integer bits) {
-    value_ |= bits;
-    return *this;
-  }
-  Flags& operator&=(Integer bits) {
-    value_ &= bits;
-    return *this;
-  }
-
- private:
-  Integer value_;
-
-  PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
-};
-
-// Flags indicating what changed since last update.
-struct SurfaceUpdateFlags : public Flags<uint32_t> {
-  enum : Type {
-    None = DVR_SURFACE_UPDATE_FLAGS_NONE,
-    NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
-    BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
-    VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
-    AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
-  };
-
-  SurfaceUpdateFlags() : Base{None} {}
-  using Base::Base;
-};
-
-// Surface attribute key/value types.
-using SurfaceAttributeKey = int32_t;
-using SurfaceAttributeValue =
-    pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
-                      std::array<float, 3>, std::array<float, 4>,
-                      std::array<float, 8>, std::array<float, 16>>;
-
-// Defined surface attribute keys.
-struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
-  enum : Type {
-    // Keys in the negative integer space are interpreted by VrFlinger for
-    // direct surfaces.
-    Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
-    ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
-    Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
-
-    // Invalid key. May be used to terminate C style lists in public API code.
-    Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
-
-    // Positive keys are interpreted by the compositor only.
-    FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
-  };
-
-  SurfaceAttribute() : Base{Invalid} {}
-  using Base::Base;
-};
-
-// Collection of surface attribute key/value pairs.
-using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
-
-struct SurfaceState {
-  int32_t surface_id;
-  int32_t process_id;
-  int32_t user_id;
-
-  SurfaceAttributes surface_attributes;
-  SurfaceUpdateFlags update_flags;
-  std::vector<int32_t> queue_ids;
-
-  // Convenience accessors.
-  bool GetVisible() const {
-    bool bool_value = false;
-    GetAttribute(SurfaceAttribute::Visible, &bool_value,
-                 ValidTypes<int32_t, int64_t, bool, float>{});
-    return bool_value;
-  }
-
-  int GetZOrder() const {
-    int int_value = 0;
-    GetAttribute(SurfaceAttribute::ZOrder, &int_value,
-                 ValidTypes<int32_t, int64_t, float>{});
-    return int_value;
-  }
-
- private:
-  template <typename... Types>
-  struct ValidTypes {};
-
-  template <typename ReturnType, typename... Types>
-  bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
-                    ValidTypes<Types...>) const {
-    auto search = surface_attributes.find(key);
-    if (search != surface_attributes.end())
-      return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
-    else
-      return false;
-  }
-
-  PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
-                           surface_attributes, update_flags, queue_ids);
-};
-
-struct SurfaceInfo {
-  int surface_id;
-  bool visible;
-  int z_order;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
-};
-
-enum class ConfigFileType : uint32_t {
-  kLensMetrics,
-  kDeviceMetrics,
-  kDeviceConfiguration,
-  kDeviceEdid
-};
-
-struct DisplayProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/client";
-
-  // Op codes.
-  enum {
-    kOpGetMetrics = 0,
-    kOpGetConfigurationData,
-    kOpSetupGlobalBuffer,
-    kOpDeleteGlobalBuffer,
-    kOpGetGlobalBuffer,
-    kOpIsVrAppRunning,
-    kOpCreateSurface,
-    kOpGetSurfaceInfo,
-    kOpCreateQueue,
-    kOpSetAttributes,
-    kOpGetDisplayIdentificationPort,
-  };
-
-  // Aliases.
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  using Void = pdx::rpc::Void;
-
-  // Methods.
-  PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
-  PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
-                    std::string(ConfigFileType config_type));
-  PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
-                    kOpGetDisplayIdentificationPort, uint8_t(Void));
-  PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
-                    LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
-                                            uint64_t usage));
-  PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer,
-                    void(DvrGlobalBufferKey key));
-  PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer,
-                    LocalNativeBufferHandle(DvrGlobalBufferKey key));
-  PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
-  PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
-                    SurfaceInfo(const SurfaceAttributes& attributes));
-  PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
-  PDX_REMOTE_METHOD(
-      CreateQueue, kOpCreateQueue,
-      LocalChannelHandle(const ProducerQueueConfig& producer_config));
-  PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
-                    void(const SurfaceAttributes& attributes));
-};
-
-struct DisplayManagerProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/manager";
-
-  // Op codes.
-  enum {
-    kOpGetSurfaceState = 0,
-    kOpGetSurfaceQueue,
-  };
-
-  // Aliases.
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  using Void = pdx::rpc::Void;
-
-  // Methods.
-  PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
-                    std::vector<SurfaceState>(Void));
-  PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
-                    LocalChannelHandle(int surface_id, int queue_id));
-};
-
-struct VSyncSchedInfo {
-  int64_t vsync_period_ns;
-  int64_t timestamp_ns;
-  uint32_t next_vsync_count;
-
- private:
-  PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
-                           next_vsync_count);
-};
-
-struct VSyncProtocol {
-  // Service path.
-  static constexpr char kClientPath[] = "system/vr/display/vsync";
-
-  // Op codes.
-  enum {
-    kOpWait = 0,
-    kOpAck,
-    kOpGetLastTimestamp,
-    kOpGetSchedInfo,
-    kOpAcknowledge,
-  };
-
-  // Aliases.
-  using Void = pdx::rpc::Void;
-  using Timestamp = int64_t;
-
-  // Methods.
-  PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
-  PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
-  PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
-  PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
-};
-
-}  // namespace display
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
deleted file mode 100644
index 20541a6..0000000
--- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-
-#include <assert.h>
-#include <tuple>
-
-#include <libbroadcastring/broadcast_ring.h>
-#include <private/dvr/display_client.h>
-
-namespace android {
-namespace dvr {
-
-// The buffer usage type for mapped shared buffers.
-enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY };
-
-// Holds the memory for the mapped shared buffer. Unlocks and releases the
-// underlying IonBuffer in destructor.
-class CPUMappedBuffer {
- public:
-  // This constructor will create a display client and get the buffer from it.
-  CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode);
-
-  // If you already have the IonBuffer, use this. It will take ownership.
-  CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode);
-
-  // Use this if you do not want to take ownership.
-  CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode);
-
-  ~CPUMappedBuffer();
-
-  // Getters.
-  size_t Size() const { return size_; }
-  void* Address() const { return address_; }
-  bool IsMapped() const { return Address() != nullptr; }
-
-  // Attempt mapping this buffer to the CPU addressable space.
-  // This will create a display client and see if the buffer exists.
-  // If the buffer has not been setup yet, you will need to try again later.
-  void TryMapping();
-
- protected:
-  // The memory area if we managed to map it.
-  size_t size_ = 0;
-  void* address_ = nullptr;
-
-  // If we are polling the display client, the buffer key here.
-  DvrGlobalBufferKey buffer_key_;
-
-  // If we just own the IonBuffer outright, it's here.
-  std::unique_ptr<IonBuffer> owned_buffer_ = nullptr;
-
-  // The last time we connected to the display service.
-  int64_t last_display_service_connection_ns_ = 0;
-
-  // If we do not own the IonBuffer, it's here
-  IonBuffer* buffer_ = nullptr;
-
-  // The usage mode.
-  CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN;
-};
-
-// Represents a broadcast ring inside a mapped shared memory buffer.
-// If has the same set of constructors as CPUMappedBuffer.
-// The template argument is the concrete BroadcastRing class that this buffer
-// holds.
-template <class RingType>
-class CPUMappedBroadcastRing : public CPUMappedBuffer {
- public:
-  CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode)
-      : CPUMappedBuffer(key, mode) {}
-
-  CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode)
-      : CPUMappedBuffer(std::move(buffer), mode) {}
-
-  CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode)
-      : CPUMappedBuffer(buffer, mode) {}
-
-  // Helper function for publishing records in the ring.
-  void Publish(const typename RingType::Record& record) {
-    assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) ||
-           (usage_mode_ == CPUUsageMode::WRITE_RARELY));
-
-    auto ring = Ring();
-    if (ring) {
-      ring->Put(record);
-    }
-  }
-
-  // Helper function for getting records from the ring.
-  // Returns true if we were able to retrieve the latest.
-  bool GetNewest(typename RingType::Record* record) {
-    assert((usage_mode_ == CPUUsageMode::READ_OFTEN) ||
-           (usage_mode_ == CPUUsageMode::READ_RARELY));
-
-    auto ring = Ring();
-    if (ring) {
-      return ring->GetNewest(&sequence_, record);
-    }
-
-    return false;
-  }
-
-  // Try obtaining the ring. If the named buffer has not been created yet, it
-  // will return nullptr.
-  RingType* Ring() {
-    // No ring created yet?
-    if (ring_ == nullptr) {
-      // Not mapped the memory yet?
-      if (IsMapped() == false) {
-        TryMapping();
-      }
-
-      // If have the memory mapped, allocate the ring.
-      if (IsMapped()) {
-        switch (usage_mode_) {
-          case CPUUsageMode::READ_OFTEN:
-          case CPUUsageMode::READ_RARELY: {
-            RingType ring;
-            bool import_ok;
-            std::tie(ring, import_ok) = RingType::Import(address_, size_);
-            if (import_ok) {
-              ring_ = std::make_unique<RingType>(ring);
-            }
-          } break;
-          case CPUUsageMode::WRITE_OFTEN:
-          case CPUUsageMode::WRITE_RARELY:
-            ring_ =
-                std::make_unique<RingType>(RingType::Create(address_, size_));
-            break;
-        }
-      }
-    }
-
-    return ring_.get();
-  }
-
- protected:
-  std::unique_ptr<RingType> ring_ = nullptr;
-
-  uint32_t sequence_ = 0;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
deleted file mode 100644
index 152464a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
-#define ANDROID_DVR_VSYNC_SERVICE_H_
-
-#include <binder/IInterface.h>
-
-namespace android {
-namespace dvr {
-
-class IVsyncCallback : public IInterface {
- public:
-  DECLARE_META_INTERFACE(VsyncCallback)
-
-  enum {
-    ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
-  };
-
-  virtual status_t onVsync(int64_t vsync_timestamp) = 0;
-};
-
-class BnVsyncCallback : public BnInterface<IVsyncCallback> {
- public:
-  virtual status_t onTransact(uint32_t code, const Parcel& data,
-                              Parcel* reply, uint32_t flags = 0);
-};
-
-// Register a callback with IVsyncService to be notified of vsync events and
-// timestamps. There's also a shared memory vsync buffer defined in
-// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
-// memory buffer that make it preferable in certain situations:
-//
-// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
-// is always available as long as surface flinger is running.
-//
-// 2. IVsyncService will make a binder callback when a vsync event occurs. This
-// allows the client to not write code to implement periodic "get the latest
-// vsync" calls, which is necessary with the vsync shared memory buffer.
-//
-// 3. The IVsyncService provides the real vsync timestamp reported by hardware
-// composer, whereas the vsync shared memory buffer only has predicted vsync
-// times.
-class IVsyncService : public IInterface {
-public:
-  DECLARE_META_INTERFACE(VsyncService)
-
-  static const char* GetServiceName() { return "vrflinger_vsync"; }
-
-  enum {
-    REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
-    UNREGISTER_CALLBACK
-  };
-
-  virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
-  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
-};
-
-class BnVsyncService : public BnInterface<IVsyncService> {
- public:
-  virtual status_t onTransact(uint32_t code, const Parcel& data,
-                              Parcel* reply, uint32_t flags = 0);
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp
deleted file mode 100644
index 6ebf487..0000000
--- a/libs/vr/libdisplay/shared_buffer_helpers.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-// We will not poll the display service for buffers more frequently than this.
-constexpr size_t kDisplayServiceTriesPerSecond = 2;
-}  // namespace
-
-CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode)
-    : buffer_key_(key), usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer,
-                                 CPUUsageMode mode)
-    : owned_buffer_(std::move(buffer)),
-      buffer_(owned_buffer_.get()),
-      usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode)
-    : buffer_(buffer), usage_mode_(mode) {
-  TryMapping();
-}
-
-CPUMappedBuffer::~CPUMappedBuffer() {
-  if (IsMapped()) {
-    buffer_->Unlock();
-  }
-}
-
-void CPUMappedBuffer::TryMapping() {
-  // Do we have an IonBuffer for this shared memory object?
-  if (buffer_ == nullptr) {
-    // Has it been too long since we last connected to the display service?
-    const auto current_time_ns = GetSystemClockNs();
-    if ((current_time_ns - last_display_service_connection_ns_) <
-        (1e9 / kDisplayServiceTriesPerSecond)) {
-      // Early exit.
-      return;
-    }
-    last_display_service_connection_ns_ = current_time_ns;
-
-    // Create a display client and get the buffer.
-    auto display_client = display::DisplayClient::Create();
-    if (display_client) {
-      auto get_result = display_client->GetGlobalBuffer(buffer_key_);
-      if (get_result.ok()) {
-        owned_buffer_ = get_result.take();
-        buffer_ = owned_buffer_.get();
-      } else {
-        // The buffer has not been created yet. This is OK, we will keep
-        // retrying.
-      }
-    } else {
-      ALOGE("Unable to create display client for shared buffer access");
-    }
-  }
-
-  if (buffer_) {
-    auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK &
-                 ~GRALLOC_USAGE_SW_WRITE_MASK;
-
-    // Figure out the usage bits.
-    switch (usage_mode_) {
-      case CPUUsageMode::READ_OFTEN:
-        usage |= GRALLOC_USAGE_SW_READ_OFTEN;
-        break;
-      case CPUUsageMode::READ_RARELY:
-        usage |= GRALLOC_USAGE_SW_READ_RARELY;
-        break;
-      case CPUUsageMode::WRITE_OFTEN:
-        usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
-        break;
-      case CPUUsageMode::WRITE_RARELY:
-        usage |= GRALLOC_USAGE_SW_WRITE_RARELY;
-        break;
-    }
-
-    int width = static_cast<int>(buffer_->width());
-    int height = 1;
-    const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_);
-
-    if (ret < 0 || !address_) {
-      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_);
-      buffer_->Unlock();
-    } else {
-      size_ = width;
-    }
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/system/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
deleted file mode 100644
index 04d4f30..0000000
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "include/private/dvr/vsync_service.h"
-
-#include <binder/Parcel.h>
-#include <log/log.h>
-
-namespace android {
-namespace dvr {
-
-status_t BnVsyncCallback::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-  switch (code) {
-    case ON_VSYNC: {
-      CHECK_INTERFACE(IVsyncCallback, data, reply);
-      int64_t vsync_timestamp = 0;
-      status_t result = data.readInt64(&vsync_timestamp);
-      if (result != OK) {
-        ALOGE("onVsync failed to readInt64: %d", result);
-        return result;
-      }
-      onVsync(vsync_timestamp);
-      return OK;
-    }
-    default: {
-      return BBinder::onTransact(code, data, reply, flags);
-    }
-  }
-}
-
-class BpVsyncCallback : public BpInterface<IVsyncCallback> {
-public:
-  explicit BpVsyncCallback(const sp<IBinder>& impl)
-      : BpInterface<IVsyncCallback>(impl) {}
-  virtual ~BpVsyncCallback() {}
-
-  virtual status_t onVsync(int64_t vsync_timestamp) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncCallback::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("onVsync failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeInt64(vsync_timestamp);
-    if (result != OK) {
-      ALOGE("onVsync failed to writeInt64: %d", result);
-      return result;
-    }
-    result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply,
-                                IBinder::FLAG_ONEWAY);
-    if (result != OK) {
-      ALOGE("onVsync failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
-
-
-status_t BnVsyncService::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-  switch (code) {
-    case REGISTER_CALLBACK: {
-      CHECK_INTERFACE(IVsyncService, data, reply);
-      sp<IBinder> callback;
-      status_t result = data.readStrongBinder(&callback);
-      if (result != OK) {
-        ALOGE("registerCallback failed to readStrongBinder: %d", result);
-        return result;
-      }
-      registerCallback(interface_cast<IVsyncCallback>(callback));
-      return OK;
-    }
-    case UNREGISTER_CALLBACK: {
-      CHECK_INTERFACE(IVsyncService, data, reply);
-      sp<IBinder> callback;
-      status_t result = data.readStrongBinder(&callback);
-      if (result != OK) {
-        ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
-        return result;
-      }
-      unregisterCallback(interface_cast<IVsyncCallback>(callback));
-      return OK;
-    }
-    default: {
-      return BBinder::onTransact(code, data, reply, flags);
-    }
-  }
-}
-
-class BpVsyncService : public BpInterface<IVsyncService> {
-public:
-  explicit BpVsyncService(const sp<IBinder>& impl)
-      : BpInterface<IVsyncService>(impl) {}
-  virtual ~BpVsyncService() {}
-
-  virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncService::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != OK) {
-      ALOGE("registerCallback failed to writeStrongBinder: %d", result);
-      return result;
-    }
-    result = remote()->transact(
-        BnVsyncService::REGISTER_CALLBACK, data, &reply);
-    if (result != OK) {
-      ALOGE("registerCallback failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-
-  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
-    Parcel data, reply;
-    status_t result = data.writeInterfaceToken(
-        IVsyncService::getInterfaceDescriptor());
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
-      return result;
-    }
-    result = data.writeStrongBinder(IInterface::asBinder(callback));
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
-      return result;
-    }
-    result = remote()->transact(
-        BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
-    if (result != OK) {
-      ALOGE("unregisterCallback failed to transact: %d", result);
-      return result;
-    }
-    return result;
-  }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
deleted file mode 100644
index 40a5099..0000000
--- a/libs/vr/libvrsensor/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
-    "pose_client.cpp",
-    "latency_model.cpp",
-]
-
-includeFiles = [
-    "include",
-]
-
-staticLibraries = [
-    "libdisplay",
-    "libdvrcommon",
-    "libbroadcastring",
-]
-
-sharedLibraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libhardware",
-    "liblog",
-    "libutils",
-    "libui",
-    "libpdx_default_transport",
-]
-
-cc_library {
-    srcs: sourceFiles,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-macro-redefined",
-    ],
-    export_include_dirs: includeFiles,
-    static_libs: staticLibraries,
-    shared_libs: sharedLibraries,
-    header_libs: ["libdvr_headers"],
-    name: "libvrsensor",
-}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libvrsensor/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
deleted file mode 100644
index b663a67..0000000
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ /dev/null
@@ -1,176 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_H_
-#define ANDROID_DVR_POSE_CLIENT_H_
-
-#ifdef __ARM_NEON
-#include <arm_neon.h>
-#else
-#ifndef __FLOAT32X4T_86
-#define __FLOAT32X4T_86
-typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
-#endif
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <dvr/dvr_pose.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-// Returned by the async pose ring buffer access API.
-typedef struct DvrPoseRingBufferInfo {
-  // Read-only pointer to the pose ring buffer. The current pose is in this
-  // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
-  // frame's forecasted pose is at element
-  // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
-  // predicted for when 50% of the corresponding frame's pixel data is visible
-  // to the user.
-  // The last value returned by dvrPresent is the count for the next frame,
-  // which is the earliest that the application could display something if they
-  // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
-  volatile const DvrPoseAsync* buffer;
-  // Minimum number of accurate forecasted poses including the current frame's
-  // pose. This is the number of poses that are udpated by the pose service.
-  // If the application reads past this count, they will get a stale prediction
-  // from a previous frame. Guaranteed to be at least 2.
-  uint32_t min_future_count;
-  // Number of elements in buffer. At least 8 and greater than min_future_count.
-  // Guaranteed to be a power of two. The total size of the buffer in bytes is:
-  //   total_count * sizeof(DvrPoseAsync)
-  uint32_t total_count;
-} DvrPoseRingBufferInfo;
-
-typedef enum DvrPoseMode {
-  DVR_POSE_MODE_6DOF = 0,
-  DVR_POSE_MODE_3DOF,
-  DVR_POSE_MODE_MOCK_FROZEN,
-  DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
-  DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
-  DVR_POSE_MODE_MOCK_ROTATE_SLOW,
-  DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
-  DVR_POSE_MODE_MOCK_ROTATE_FAST,
-  DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
-  DVR_POSE_MODE_FLOAT,
-  DVR_POSE_MODE_MOCK_MOTION_SICKNESS,
-
-  // Always last.
-  DVR_POSE_MODE_COUNT,
-} DvrPoseMode;
-
-typedef enum DvrControllerId {
-  DVR_CONTROLLER_0 = 0,
-  DVR_CONTROLLER_1 = 1,
-} DvrControllerId;
-
-// Creates a new pose client.
-//
-// @return Pointer to the created pose client, nullptr on failure.
-DvrPoseClient* dvrPoseClientCreate();
-
-// Destroys a pose client.
-//
-// @param client Pointer to the pose client to be destroyed.
-void dvrPoseClientDestroy(DvrPoseClient* client);
-
-// Gets the pose for the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-//     Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
-                     DvrPoseAsync* out_pose);
-
-// Gets the current vsync count.
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client);
-
-// Gets the pose for the given controller at the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param controller_id The controller id.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-//     Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
-                               uint32_t vsync_count, DvrPoseAsync* out_pose);
-
-// Enables/disables logging for the controller fusion.
-//
-// @param client Pointer to the pose client.
-// @param enable True starts logging, False stops.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable);
-
-// DEPRECATED
-// Polls current pose state.
-//
-// @param client Pointer to the pose client.
-// @param state Struct to store polled state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state);
-
-// Freezes the pose to the provided state.
-//
-// Future poll operations will return this state until a different state is
-// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is
-// not frozen.
-//
-// @param client Pointer to the pose client.
-// @param frozen_state State pose to be frozen to.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state);
-
-// Sets the pose service mode.
-//
-// @param mode The requested pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode);
-
-// Gets the pose service mode.
-//
-// @param mode Return value for the current pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode);
-
-// Get access to the shared memory pose ring buffer.
-// A future pose at vsync <current> + <offset> is accessed at index:
-//   index = (<current> + <offset>) % out_buffer_size
-// Where <current> was the last value returned by dvrPresent and
-// <offset> is less than or equal to |out_min_future_count|.
-// |out_buffer| will be set to a pointer to the buffer.
-// |out_fd| will be set to the gralloc buffer file descriptor, which is
-//   required for binding this buffer for GPU use.
-// Returns 0 on success.
-int dvrPoseClientGetRingBuffer(DvrPoseClient* client,
-                               DvrPoseRingBufferInfo* out_info);
-
-// Sets enabled state for sensors pose processing.
-//
-// @param enabled Whether sensors are enabled or disabled.
-// @return Zero on success
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
-
-// Requests a burst of data samples from pose service. The data samples are
-// passed through a shared memory buffer obtained by calling
-// dvrPoseClientGetDataReader().
-//
-// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
-// @return Zero on success.
-int dvrPoseClientDataCapture(DvrPoseClient* client,
-                             const DvrPoseDataCaptureRequest* request);
-
-// Destroys the write buffer queue for the given |data_type|.
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
deleted file mode 100644
index bf0e687..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_LATENCY_MODEL_H_
-#define ANDROID_DVR_LATENCY_MODEL_H_
-
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-// This class models the latency from sensors. It will look at the first
-// window_size measurements and return their average after that.
-class LatencyModel {
- public:
-  explicit LatencyModel(size_t window_size);
-  ~LatencyModel() = default;
-
-  void AddLatency(int64_t latency_ns);
-  int64_t CurrentLatencyEstimate() const { return latency_; }
-
- private:
-  size_t window_size_;
-  int64_t latency_sum_ = 0;
-  size_t num_summed_ = 0;
-  int64_t latency_ = 0;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
deleted file mode 100644
index 7bf1cd4..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_POSE_IPC_H_
-#define ANDROID_DVR_POSE_IPC_H_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DVR_POSE_SERVICE_BASE "system/vr/pose"
-#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
-
-enum {
-  DVR_POSE_FREEZE = 0,
-  DVR_POSE_SET_MODE,
-  DVR_POSE_GET_MODE,
-  DVR_POSE_GET_CONTROLLER_RING_BUFFER,
-  DVR_POSE_LOG_CONTROLLER,
-  DVR_POSE_SENSORS_ENABLE,
-  DVR_POSE_GET_TANGO_READER,
-  DVR_POSE_DATA_CAPTURE,
-  DVR_POSE_TANGO_READER_DESTROY,
-};
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
deleted file mode 100644
index 39592bb..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-
-#include <private/dvr/buffer_hub_queue_client.h>
-
-using android::dvr::ConsumerQueue;
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-namespace android {
-namespace dvr {
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
-                                     ConsumerQueue **queue_out);
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
deleted file mode 100644
index d3a4521..0000000
--- a/libs/vr/libvrsensor/latency_model.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <private/dvr/latency_model.h>
-
-#include <cmath>
-
-namespace android {
-namespace dvr {
-
-LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
-
-void LatencyModel::AddLatency(int64_t latency_ns) {
-  // Not enough samples yet?
-  if (num_summed_ < window_size_) {
-    // Accumulate.
-    latency_sum_ += latency_ns;
-
-    // Have enough samples for latency estimate?
-    if (++num_summed_ == window_size_) {
-      latency_ = latency_sum_ / window_size_;
-    }
-  }
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
deleted file mode 100644
index 4ff6a09..0000000
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-#define LOG_TAG "PoseClient"
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/pose_client.h>
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <pdx/client.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/pose-ipc.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-using android::dvr::ConsumerQueue;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace {
-
-typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing;
-
-constexpr static int32_t MAX_CONTROLLERS = 2;
-}  // namespace
-
-// PoseClient is a remote interface to the pose service in sensord.
-class PoseClient : public pdx::ClientBase<PoseClient> {
- public:
-  ~PoseClient() override {}
-
-  // Casts C handle into an instance of this class.
-  static PoseClient* FromC(DvrPoseClient* client) {
-    return reinterpret_cast<PoseClient*>(client);
-  }
-
-  // Polls the pose service for the current state and stores it in *state.
-  // Returns zero on success, a negative error code otherwise.
-  int Poll(DvrPose* state) {
-    // Allocate the helper class to access the sensor pose buffer.
-    if (sensor_pose_buffer_ == nullptr) {
-      sensor_pose_buffer_ = std::make_unique<SensorPoseRing>(
-          DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY);
-    }
-
-    if (state) {
-      if (sensor_pose_buffer_->GetNewest(state)) {
-        return 0;
-      } else {
-        return -EAGAIN;
-      }
-    }
-
-    return -EINVAL;
-  }
-
-  int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      *out_pose =
-          vsync_buffer
-              ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
-      return 0;
-    } else {
-      return -EAGAIN;
-    }
-  }
-
-  uint32_t GetVsyncCount() {
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      return vsync_buffer->vsync_count;
-    }
-
-    return 0;
-  }
-
-  int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
-                        DvrPoseAsync* out_pose) {
-    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
-      return -EINVAL;
-    }
-    if (!controllers_[controller_id].mapped_pose_buffer) {
-      int ret = GetControllerRingBuffer(controller_id);
-      if (ret < 0)
-        return ret;
-    }
-    *out_pose =
-        controllers_[controller_id]
-            .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
-    return 0;
-  }
-
-  int LogController(bool enable) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
-                                         sizeof(enable), nullptr, 0);
-    ALOGE_IF(!status, "Pose LogController() failed because: %s",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Freezes the pose to the provided state. Future poll operations will return
-  // this state until a different state is frozen or SetMode() is called with a
-  // different mode.
-  // Returns zero on success, a negative error code otherwise.
-  int Freeze(const DvrPose& frozen_state) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
-                                         sizeof(frozen_state), nullptr, 0);
-    ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Sets the data mode for the pose service.
-  int SetMode(DvrPoseMode mode) {
-    Transaction trans{*this};
-    Status<int> status =
-        trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
-    ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Gets the data mode for the pose service.
-  int GetMode(DvrPoseMode* out_mode) {
-    int mode;
-    Transaction trans{*this};
-    Status<int> status =
-        trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
-    ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
-             status.GetErrorMessage().c_str());
-    if (status)
-      *out_mode = DvrPoseMode(mode);
-    return ReturnStatusOrError(status);
-  }
-
-  int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
-    // Get buffer.
-    Transaction trans{*this};
-    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
-        DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
-
-    if (!status) {
-      ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
-            status.GetErrorMessage().c_str());
-      *queue_out = nullptr;
-      return -status.error();
-    }
-
-    std::unique_ptr<ConsumerQueue> consumer_queue =
-        ConsumerQueue::Import(status.take());
-    *queue_out = consumer_queue.release();
-    return 0;
-  }
-
-  int DataCapture(const DvrPoseDataCaptureRequest* request) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
-                                         sizeof(*request), nullptr, 0);
-    ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  int DataReaderDestroy(uint64_t data_type) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
-                                         &data_type, sizeof(data_type), nullptr,
-                                         0);
-    ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  // Enables or disables all pose processing from sensors
-  int EnableSensors(bool enabled) {
-    Transaction trans{*this};
-    Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled,
-                                         sizeof(enabled), nullptr, 0);
-    ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n",
-             status.GetErrorMessage().c_str());
-    return ReturnStatusOrError(status);
-  }
-
-  int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
-    // First time mapping the buffer?
-    const auto vsync_buffer = GetVsyncBuffer();
-    if (vsync_buffer) {
-      if (out_info) {
-        out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount;
-        out_info->total_count = DvrVsyncPoseBuffer::kSize;
-        out_info->buffer = vsync_buffer->vsync_poses;
-      }
-      return -EINVAL;
-    }
-
-    return -EAGAIN;
-  }
-
-  int GetControllerRingBuffer(int32_t controller_id) {
-    if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
-      return -EINVAL;
-    }
-    ControllerClientState& client_state = controllers_[controller_id];
-    if (client_state.pose_buffer.get()) {
-      return 0;
-    }
-
-    Transaction trans{*this};
-    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
-        DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
-        sizeof(controller_id), nullptr, 0);
-    if (!status) {
-      return -status.error();
-    }
-
-    auto buffer = ConsumerBuffer::Import(status.take());
-    if (!buffer) {
-      ALOGE("Pose failed to import ring buffer");
-      return -EIO;
-    }
-    constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
-    void* addr = nullptr;
-    int ret = buffer->GetBlobReadWritePointer(size, &addr);
-    if (ret < 0 || !addr) {
-      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
-      return -EIO;
-    }
-    client_state.pose_buffer.swap(buffer);
-    client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
-    ALOGI(
-        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
-        controller_id, client_state.mapped_pose_buffer[0].position[0],
-        client_state.mapped_pose_buffer[0].position[1],
-        client_state.mapped_pose_buffer[0].position[2],
-        client_state.mapped_pose_buffer[0].orientation[0],
-        client_state.mapped_pose_buffer[0].orientation[1],
-        client_state.mapped_pose_buffer[0].orientation[2],
-        client_state.mapped_pose_buffer[0].orientation[3]);
-    return 0;
-  }
-
- private:
-  friend BASE;
-
-  // Set up a channel to the pose service.
-  PoseClient()
-      : BASE(pdx::default_transport::ClientChannelFactory::Create(
-            DVR_POSE_SERVICE_CLIENT)) {
-    // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
-    // block while waiting for the pose service to come back up.
-    EnableAutoReconnect(kInfiniteTimeout);
-  }
-
-  PoseClient(const PoseClient&) = delete;
-  PoseClient& operator=(const PoseClient&) = delete;
-
-  const DvrVsyncPoseBuffer* GetVsyncBuffer() {
-    if (mapped_vsync_pose_buffer_ == nullptr) {
-      if (vsync_pose_buffer_ == nullptr) {
-        // The constructor tries mapping it so we do not need TryMapping after.
-        vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>(
-            DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN);
-      } else if (vsync_pose_buffer_->IsMapped() == false) {
-        vsync_pose_buffer_->TryMapping();
-      }
-
-      if (vsync_pose_buffer_->IsMapped()) {
-        mapped_vsync_pose_buffer_ =
-            static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address());
-      }
-    }
-
-    return mapped_vsync_pose_buffer_;
-  }
-
-  // The vsync pose buffer if already mapped.
-  std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_;
-
-  // The direct sensor pose buffer.
-  std::unique_ptr<SensorPoseRing> sensor_pose_buffer_;
-
-  const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
-
-  struct ControllerClientState {
-    std::unique_ptr<ConsumerBuffer> pose_buffer;
-    const DvrPoseAsync* mapped_pose_buffer = nullptr;
-  };
-  ControllerClientState controllers_[MAX_CONTROLLERS];
-};
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
-                                     ConsumerQueue** queue_out) {
-  return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
-}
-
-}  // namespace dvr
-}  // namespace android
-
-using android::dvr::PoseClient;
-
-extern "C" {
-
-DvrPoseClient* dvrPoseClientCreate() {
-  auto* client = PoseClient::Create().release();
-  return reinterpret_cast<DvrPoseClient*>(client);
-}
-
-void dvrPoseClientDestroy(DvrPoseClient* client) {
-  delete PoseClient::FromC(client);
-}
-
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
-                     DvrPoseAsync* out_pose) {
-  return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
-}
-
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) {
-  return PoseClient::FromC(client)->GetVsyncCount();
-}
-
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
-                               uint32_t vsync_count, DvrPoseAsync* out_pose) {
-  return PoseClient::FromC(client)->GetControllerPose(controller_id,
-                                                      vsync_count, out_pose);
-}
-
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable) {
-  return PoseClient::FromC(client)->LogController(enable);
-}
-
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) {
-  return PoseClient::FromC(client)->Poll(state);
-}
-
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) {
-  return PoseClient::FromC(client)->Freeze(*frozen_state);
-}
-
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) {
-  return PoseClient::FromC(client)->SetMode(mode);
-}
-
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) {
-  return PoseClient::FromC(client)->GetMode(mode);
-}
-
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
-  return PoseClient::FromC(client)->EnableSensors(enabled);
-}
-
-int dvrPoseClientDataCapture(DvrPoseClient* client,
-                             const DvrPoseDataCaptureRequest* request) {
-  return PoseClient::FromC(client)->DataCapture(request);
-}
-
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
-  return PoseClient::FromC(client)->DataReaderDestroy(data_type);
-}
-
-}  // extern "C"
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index b3b4fb1..3d7242e 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -15,6 +15,9 @@
       "name": "inputflinger_tests"
     },
     {
+      "name": "libchrome-gestures_test"
+    },
+    {
       "name": "libpalmrejection_test"
     },
     {
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index d210e9e..512cb6e 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -24,7 +24,7 @@
 
 /* Specifies which events are to be canceled and why. */
 struct CancelationOptions {
-    enum Mode {
+    enum class Mode {
         CANCEL_ALL_EVENTS = 0,
         CANCEL_POINTER_EVENTS = 1,
         CANCEL_NON_POINTER_EVENTS = 2,
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ec9701a..7bbfb95 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -166,7 +166,7 @@
         repeatCount(repeatCount),
         downTime(downTime),
         syntheticRepeat(false),
-        interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+        interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
         interceptKeyWakeupTime(0) {}
 
 KeyEntry::~KeyEntry() {}
@@ -189,7 +189,7 @@
 
     dispatchInProgress = false;
     syntheticRepeat = false;
-    interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+    interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
     interceptKeyWakeupTime = 0;
 }
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f801912..3799814 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -140,11 +140,11 @@
 
     bool syntheticRepeat; // set to true for synthetic key repeats
 
-    enum InterceptKeyResult {
-        INTERCEPT_KEY_RESULT_UNKNOWN,
-        INTERCEPT_KEY_RESULT_SKIP,
-        INTERCEPT_KEY_RESULT_CONTINUE,
-        INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
+    enum class InterceptKeyResult {
+        UNKNOWN,
+        SKIP,
+        CONTINUE,
+        TRY_AGAIN_LATER,
     };
     InterceptKeyResult interceptKeyResult; // set based on the interception result
     nsecs_t interceptKeyWakeupTime;        // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index eb938c8..4aac377 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -60,7 +60,6 @@
 using android::gui::TouchOcclusionMode;
 using android::gui::WindowInfo;
 using android::gui::WindowInfoHandle;
-using android::os::IInputConstants;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
 
@@ -149,21 +148,23 @@
 }
 
 bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
+    switch (MotionEvent::getActionMasked(action)) {
         case AMOTION_EVENT_ACTION_DOWN:
         case AMOTION_EVENT_ACTION_UP:
-        case AMOTION_EVENT_ACTION_CANCEL:
+            return pointerCount == 1;
         case AMOTION_EVENT_ACTION_MOVE:
-        case AMOTION_EVENT_ACTION_OUTSIDE:
         case AMOTION_EVENT_ACTION_HOVER_ENTER:
         case AMOTION_EVENT_ACTION_HOVER_MOVE:
         case AMOTION_EVENT_ACTION_HOVER_EXIT:
+            return pointerCount >= 1;
+        case AMOTION_EVENT_ACTION_CANCEL:
+        case AMOTION_EVENT_ACTION_OUTSIDE:
         case AMOTION_EVENT_ACTION_SCROLL:
             return true;
         case AMOTION_EVENT_ACTION_POINTER_DOWN:
         case AMOTION_EVENT_ACTION_POINTER_UP: {
-            int32_t index = getMotionEventActionPointerIndex(action);
-            return index >= 0 && index < pointerCount;
+            const int32_t index = MotionEvent::getActionIndex(action);
+            return index >= 0 && index < pointerCount && pointerCount > 1;
         }
         case AMOTION_EVENT_ACTION_BUTTON_PRESS:
         case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
@@ -1033,8 +1034,8 @@
                 KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
                 if (pendingKey.keyCode == keyEntry.keyCode &&
                     pendingKey.interceptKeyResult ==
-                            KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
-                    pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+                            KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
+                    pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
                     pendingKey.interceptKeyWakeupTime = 0;
                     needWake = true;
                 }
@@ -1169,17 +1170,18 @@
 
     switch (entry.type) {
         case EventEntry::Type::KEY: {
-            CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+            CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
             synthesizeCancelationEventsForAllConnectionsLocked(options);
             break;
         }
         case EventEntry::Type::MOTION: {
             const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
             if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
-                CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+                CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
                 synthesizeCancelationEventsForAllConnectionsLocked(options);
             } else {
-                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+                CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+                                           reason);
                 synthesizeCancelationEventsForAllConnectionsLocked(options);
             }
             break;
@@ -1334,7 +1336,7 @@
         resetKeyRepeatLocked();
     }
 
-    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
+    CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
     options.deviceId = entry.deviceId;
     synthesizeCancelationEventsForAllConnectionsLocked(options);
     return true;
@@ -1539,19 +1541,19 @@
     }
 
     // Handle case where the policy asked us to try again later last time.
-    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
+    if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
         if (currentTime < entry->interceptKeyWakeupTime) {
             if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                 *nextWakeupTime = entry->interceptKeyWakeupTime;
             }
             return false; // wait until next wakeup
         }
-        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+        entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
         entry->interceptKeyWakeupTime = 0;
     }
 
     // Give the policy a chance to intercept the key.
-    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+    if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             sp<IBinder> focusedWindowToken =
                     mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
@@ -1562,9 +1564,9 @@
             postCommandLocked(std::move(command));
             return false; // wait for the command to run
         } else {
-            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+            entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
         }
-    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+    } else if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::SKIP) {
         if (*dropReason == DropReason::NOT_DROPPED) {
             *dropReason = DropReason::POLICY;
         }
@@ -1723,9 +1725,9 @@
         return true;
     }
     if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
-        CancelationOptions::Mode mode(isPointerEvent
-                                              ? CancelationOptions::CANCEL_POINTER_EVENTS
-                                              : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+        CancelationOptions::Mode mode(
+                isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
+                               : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
         CancelationOptions options(mode, "input event injection failed");
         synthesizeCancelationEventsForMonitorsLocked(options);
         return true;
@@ -1736,7 +1738,7 @@
 
     // Dispatch the motion.
     if (conflictingPointerActions) {
-        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+        CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                    "conflicting pointer actions");
         synthesizeCancelationEventsForAllConnectionsLocked(options);
     }
@@ -1837,7 +1839,7 @@
     ALOGW("Canceling events for %s because it is unresponsive",
           connection->inputChannel->getName().c_str());
     if (connection->status == Connection::Status::NORMAL) {
-        CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+        CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
                                    "application not responding");
         synthesizeCancelationEventsForConnectionLocked(connection, options);
     }
@@ -2096,7 +2098,7 @@
 
     bool isSplit = shouldSplitTouch(tempTouchState, entry);
     const bool switchedDevice = (oldState != nullptr) &&
-            (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source);
+            (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
 
     const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                                 maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2104,6 +2106,7 @@
     const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                              maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
     const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
@@ -2306,6 +2309,20 @@
                                                  entry.eventTime);
             }
         }
+
+        // Update the pointerIds for non-splittable when it received pointer down.
+        if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+            // If no split, we suppose all touched windows should receive pointer down.
+            const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+            for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+                TouchedWindow& touchedWindow = tempTouchState.windows[i];
+                // Ignore drag window for it should just track one pointer.
+                if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+                    continue;
+                }
+                touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+            }
+        }
     }
 
     // Update dispatching for hover enter and exit.
@@ -2415,12 +2432,14 @@
                 if (info->displayId == displayId &&
                     windowHandle->getInfo()->inputConfig.test(
                             WindowInfo::InputConfig::IS_WALLPAPER)) {
+                    BitSet32 pointerIds;
+                    pointerIds.markBit(entry.pointerProperties[0].id);
                     tempTouchState.addOrUpdateWindow(windowHandle,
                                                      InputTarget::Flags::WINDOW_IS_OBSCURED |
                                                              InputTarget::Flags::
                                                                      WINDOW_IS_PARTIALLY_OBSCURED |
                                                              InputTarget::Flags::DISPATCH_AS_IS,
-                                                     BitSet32(0), entry.eventTime);
+                                                     pointerIds, entry.eventTime);
                 }
             }
         }
@@ -2480,17 +2499,6 @@
             }
             i += 1;
         }
-    } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
-        // If no split, we suppose all touched windows should receive pointer down.
-        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
-            TouchedWindow& touchedWindow = tempTouchState.windows[i];
-            // Ignore drag window for it should just track one pointer.
-            if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
-                continue;
-            }
-            touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
-        }
     }
 
     // Save changes unless the action was scroll in which case the temporary touch
@@ -2990,6 +2998,8 @@
                              connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
+    LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
+                        "No dispatch flags are set for %s", eventEntry->getDescription().c_str());
 
     const bool wasEmpty = connection->outboundQueue.empty();
 
@@ -4048,10 +4058,9 @@
                   args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
         }
     }
-    if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
-                             args->pointerProperties)) {
-        return;
-    }
+    LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+                                             args->pointerProperties),
+                        "Invalid event: %s", args->dump().c_str());
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4800,10 +4809,13 @@
     updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
 
     const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
-    if (mLastHoverWindowHandle &&
-        std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
-                windowHandles.end()) {
-        mLastHoverWindowHandle = nullptr;
+    if (mLastHoverWindowHandle) {
+        const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo();
+        if (lastHoverWindowInfo->displayId == displayId &&
+            std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+                    windowHandles.end()) {
+            mLastHoverWindowHandle = nullptr;
+        }
     }
 
     std::optional<FocusResolver::FocusChanges> changes =
@@ -4826,7 +4838,7 @@
                 std::shared_ptr<InputChannel> touchedInputChannel =
                         getInputChannelLocked(touchedWindow.windowHandle->getToken());
                 if (touchedInputChannel != nullptr) {
-                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                    CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                                "touched window was removed");
                     synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
                     // Since we are about to drop the touch, cancel the events for the wallpaper as
@@ -4872,7 +4884,7 @@
             std::shared_ptr<InputChannel> inputChannel =
                     getInputChannelLocked(newWindowHandle->getToken());
             if (inputChannel != nullptr) {
-                CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                            "touched window's orientation changed");
                 synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
             }
@@ -4953,7 +4965,7 @@
                         getInputChannelLocked(oldFocusedWindowToken);
                 if (inputChannel != nullptr) {
                     CancelationOptions
-                            options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+                            options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
                                     "The display which contains this window no longer has focus.");
                     options.displayId = ADISPLAY_ID_NONE;
                     synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
@@ -4973,10 +4985,6 @@
                 }
             }
         }
-
-        if (DEBUG_FOCUS) {
-            logDispatchStateLocked();
-        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -5007,10 +5015,6 @@
         } else {
             changed = false;
         }
-
-        if (DEBUG_FOCUS) {
-            logDispatchStateLocked();
-        }
     } // release lock
 
     if (changed) {
@@ -5180,15 +5184,11 @@
         if (fromConnection != nullptr && toConnection != nullptr) {
             fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
             CancelationOptions
-                    options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                    options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                             "transferring touch focus from this window to another window");
             synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
             synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
         }
-
-        if (DEBUG_FOCUS) {
-            logDispatchStateLocked();
-        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -5254,7 +5254,7 @@
         ALOGD("Resetting and dropping all events (%s).", reason);
     }
 
-    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
+    CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason);
     synthesizeCancelationEventsForAllConnectionsLocked(options);
 
     resetKeyRepeatLocked();
@@ -5435,9 +5435,7 @@
 
     if (!mReplacedKeys.empty()) {
         dump += INDENT "ReplacedKeys:\n";
-        for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
-            const KeyReplacement& replacement = pair.first;
-            int32_t newKeyCode = pair.second;
+        for (const auto& [replacement, newKeyCode] : mReplacedKeys) {
             dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
                                  replacement.keyCode, replacement.deviceId, newKeyCode);
         }
@@ -5681,7 +5679,7 @@
     TouchState& state = *statePtr;
     TouchedWindow& window = *windowPtr;
     // Send cancel events to all the input channels we're stealing from.
-    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+    CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                "input channel stole pointer stream");
     options.deviceId = state.deviceId;
     options.displayId = displayId;
@@ -5967,11 +5965,11 @@
     } // acquire lock
 
     if (delay < 0) {
-        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+        entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;
     } else if (delay == 0) {
-        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+        entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
     } else {
-        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+        entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;
         entry.interceptKeyWakeupTime = now() + delay;
     }
 }
@@ -6081,7 +6079,7 @@
 
             // Cancel the fallback key.
             if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
-                CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+                CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
                                            "application handled the original non-fallback key "
                                            "or is no longer a foreground target, "
                                            "canceling previously dispatched fallback key");
@@ -6158,7 +6156,7 @@
                 }
             }
 
-            CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+            CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
                                        "canceling fallback, policy no longer desires it");
             options.keyCode = fallbackKeyCode;
             synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -6311,7 +6309,7 @@
     if (changes.oldFocus) {
         std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
         if (focusedInputChannel) {
-            CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+            CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
                                        "focus left window");
             synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
             enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
@@ -6451,7 +6449,7 @@
     {
         std::scoped_lock _l(mLock);
         ALOGD("Canceling all ongoing pointer gestures on all displays.");
-        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+        CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                    "cancel current touch");
         synthesizeCancelationEventsForAllConnectionsLocked(options);
 
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 047c628..563868d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -500,10 +500,10 @@
     }
 
     switch (options.mode) {
-        case CancelationOptions::CANCEL_ALL_EVENTS:
-        case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+        case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
+        case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
             return true;
-        case CancelationOptions::CANCEL_FALLBACK_EVENTS:
+        case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
             return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
         default:
             return false;
@@ -521,11 +521,11 @@
     }
 
     switch (options.mode) {
-        case CancelationOptions::CANCEL_ALL_EVENTS:
+        case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
             return true;
-        case CancelationOptions::CANCEL_POINTER_EVENTS:
+        case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
             return memento.source & AINPUT_SOURCE_CLASS_POINTER;
-        case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+        case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
             return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
         default:
             return false;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 114e0bf..c21af9e 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -34,8 +34,7 @@
 void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
                                    ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
                                    std::optional<nsecs_t> eventTime) {
-    for (size_t i = 0; i < windows.size(); i++) {
-        TouchedWindow& touchedWindow = windows[i];
+    for (TouchedWindow& touchedWindow : windows) {
         if (touchedWindow.windowHandle == windowHandle) {
             touchedWindow.targetFlags |= targetFlags;
             if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 484b0d3..76dce63 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -125,9 +125,12 @@
 
     /**
      * Set the touch mode state.
-     * Touch mode is a global state that apps may enter / exit based on specific
-     * user interactions with input devices.
-     * If true, the device is in touch mode.
+     * Touch mode is a per display state that apps may enter / exit based on specific user
+     * interactions with input devices. If <code>inTouchMode</code> is set to true, the display
+     * identified by <code>displayId</code> will be changed to touch mode. Performs a permission
+     * check if hasPermission is set to false.
+     *
+     * This method also enqueues a a TouchModeEntry message for dispatching.
      *
      * Returns true when changing touch mode state.
      */
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3b0f2ac..b8a6dad 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -23,6 +23,7 @@
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
 #include <stddef.h>
+#include <ui/Rotation.h>
 #include <unistd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -87,6 +88,9 @@
     virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
             int32_t sw) = 0;
 
+    virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+                                 int32_t toKeyCode) const = 0;
+
     virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
 
     /* Toggle Caps Lock */
@@ -395,7 +399,7 @@
 
     /* Gets the affine calibration associated with the specified device. */
     virtual TouchAffineTransformation getTouchAffineTransformation(
-            const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+            const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
     /* Notifies the input reader policy that a stylus gesture has started. */
     virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
 };
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index f28dbf3..c46f905 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -116,6 +116,8 @@
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
+    NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
+
     bool operator==(const NotifyMotionArgs& rhs) const;
 
     std::string dump() const;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 24168a1..f37f0fa 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -50,7 +50,9 @@
         "mapper/SensorInputMapper.cpp",
         "mapper/SingleTouchInputMapper.cpp",
         "mapper/SwitchInputMapper.cpp",
+        "mapper/TouchCursorInputMapperCommon.cpp",
         "mapper/TouchInputMapper.cpp",
+        "mapper/TouchpadInputMapper.cpp",
         "mapper/VibratorInputMapper.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
@@ -58,6 +60,7 @@
         "mapper/accumulator/MultiTouchMotionAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
+        "mapper/gestures/GesturesLogging.cpp",
     ],
 }
 
@@ -69,22 +72,25 @@
         "libcap",
         "libcrypto",
         "libcutils",
+        "libjsoncpp",
         "liblog",
+        "libPlatformProperties",
         "libstatslog",
         "libutils",
     ],
     static_libs: [
         "libc++fs",
+        "libchrome-gestures",
         "libui-types",
     ],
     header_libs: [
         "libbatteryservice_headers",
+        "libchrome-gestures_headers",
         "libinputreader_headers",
     ],
     target: {
         android: {
             shared_libs: [
-                "libPlatformProperties",
                 "libinput",
             ],
         },
@@ -97,6 +103,25 @@
     },
 }
 
+cc_library_static {
+    name: "libinputreader_static",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputreader_defaults",
+    ],
+    shared_libs: [
+        "libinputflinger_base",
+    ],
+    export_header_lib_headers: [
+        "libbatteryservice_headers",
+        "libchrome-gestures_headers",
+        "libinputreader_headers",
+    ],
+    whole_static_libs: [
+        "libchrome-gestures",
+    ],
+}
+
 cc_library_shared {
     name: "libinputreader",
     host_supported: true,
@@ -111,6 +136,7 @@
         // This should consist only of dependencies from inputflinger. Other dependencies should be
         // in cc_defaults so that they are included in the tests.
         "libinputflinger_base",
+        "libjsoncpp",
     ],
     export_header_lib_headers: [
         "libinputreader_headers",
@@ -125,5 +151,6 @@
     },
     static_libs: [
         "libc++fs",
+        "libchrome-gestures",
     ],
 }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0aaef53..e26bc8c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -952,15 +952,19 @@
             device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
     switch (mapKeyRes) {
         case OK:
-            return outKeyCode;
+            break;
         case NAME_NOT_FOUND:
             // key character map doesn't re-map this scanCode, hence the keyCode remains the same
-            return locationKeyCode;
+            outKeyCode = locationKeyCode;
+            break;
         default:
             ALOGW("Failed to get key code for key location: Key character map returned error %s",
                   statusToString(mapKeyRes).c_str());
-            return AKEYCODE_UNKNOWN;
+            outKeyCode = AKEYCODE_UNKNOWN;
+            break;
     }
+    // Remap if there is a Key remapping added to the KCM and return the remapped key
+    return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode);
 }
 
 int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
@@ -1023,6 +1027,18 @@
     return false;
 }
 
+void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+    std::scoped_lock _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr) {
+        return;
+    }
+    const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+    if (kcm) {
+        kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+    }
+}
+
 status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                           int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
     std::scoped_lock _l(mLock);
@@ -1048,7 +1064,13 @@
 
         if (status == NO_ERROR) {
             if (kcm) {
-                kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
+                // Remap keys based on user-defined key remappings and key behavior defined in the
+                // corresponding kcm file
+                *outKeycode = kcm->applyKeyRemapping(*outKeycode);
+
+                // Remap keys based on Key behavior defined in KCM file
+                std::tie(*outKeycode, *outMetaState) =
+                        kcm->applyKeyBehavior(*outKeycode, metaState);
             } else {
                 *outMetaState = metaState;
             }
@@ -2215,6 +2237,10 @@
         // a touch screen.
         if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
             device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
+            if (device->propBitmask.test(INPUT_PROP_POINTER) &&
+                !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
+                device->classes |= InputDeviceClass::TOUCHPAD;
+            }
         }
         // Is this an old style single-touch driver?
     } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index e6ab872..11b5209 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -20,6 +20,7 @@
 
 #include <algorithm>
 
+#include <android/sysprop/InputProperties.sysprop.h>
 #include <ftl/flags.h>
 
 #include "CursorInputMapper.h"
@@ -33,6 +34,7 @@
 #include "SensorInputMapper.h"
 #include "SingleTouchInputMapper.h"
 #include "SwitchInputMapper.h"
+#include "TouchpadInputMapper.h"
 #include "VibratorInputMapper.h"
 
 using android::hardware::input::InputDeviceCountryCode;
@@ -208,7 +210,12 @@
     }
 
     // Touchscreens and touchpad devices.
-    if (classes.test(InputDeviceClass::TOUCH_MT)) {
+    static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+    if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+        classes.test(InputDeviceClass::TOUCH_MT)) {
+        mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
+    } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
     } else if (classes.test(InputDeviceClass::TOUCH)) {
         mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
@@ -618,6 +625,12 @@
     });
 }
 
+void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+    for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
+        context.addKeyRemapping(fromKeyCode, toKeyCode);
+    });
+}
+
 void InputDevice::bumpGeneration() {
     mGeneration = mContext->bumpGeneration();
 }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f04a646..57f679c 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -650,6 +650,15 @@
     return result;
 }
 
+void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device != nullptr) {
+        device->addKeyRemapping(fromKeyCode, toKeyCode);
+    }
+}
+
 int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8e5f15f..8a844b2 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -94,7 +94,7 @@
     /* The input device is a cursor device such as a trackball or mouse. */
     CURSOR = 0x00000008,
 
-    /* The input device is a multi-touch touchscreen. */
+    /* The input device is a multi-touch touchscreen or touchpad. */
     TOUCH_MT = 0x00000010,
 
     /* The input device is a directional pad (implies keyboard, has DPAD keys). */
@@ -130,6 +130,9 @@
     /* The input device has sysfs controllable lights */
     LIGHT = 0x00008000,
 
+    /* The input device is a touchpad, requiring an on-screen cursor. */
+    TOUCHPAD = 0x00010000,
+
     /* The input device is virtual (not a real device, not part of UI configuration). */
     VIRTUAL = 0x40000000,
 
@@ -259,6 +262,9 @@
 
     virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
 
+    virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+                                 int32_t toKeyCode) const = 0;
+
     virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
                             int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
                             uint32_t* outFlags) const = 0;
@@ -457,6 +463,9 @@
 
     bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
 
+    void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+                         int32_t toKeyCode) const override final;
+
     status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                     int32_t* outKeycode, int32_t* outMetaState,
                     uint32_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 439123b..6fa21e5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -114,6 +114,8 @@
     int32_t getMetaState();
     void updateMetaState(int32_t keyCode);
 
+    void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
     void bumpGeneration();
 
     [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -278,6 +280,10 @@
 
     inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
 
+    inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
+        mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+    }
+
     inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
                            int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
         return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f2503a..e9c989a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -68,6 +68,8 @@
     int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
     int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
 
+    void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
+
     int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
 
     void toggleCapsLockState(int32_t deviceId) override;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index a1a2af9..13e4d0c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -227,7 +227,7 @@
             mDisplayId = mPointerController->getDisplayId();
         }
 
-        mOrientation = DISPLAY_ORIENTATION_0;
+        mOrientation = ui::ROTATION_0;
         const bool isOrientedDevice =
                 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
         // InputReader works in the un-rotated display coordinate space, so we don't need to do
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 20746e5..939cceb 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -22,6 +22,7 @@
 
 #include <PointerControllerInterface.h>
 #include <input/VelocityControl.h>
+#include <ui/Rotation.h>
 
 namespace android {
 
@@ -115,7 +116,7 @@
     // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
     // std::nullopt), all events will be ignored.
     std::optional<int32_t> mDisplayId;
-    int32_t mOrientation;
+    ui::Rotation mOrientation;
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index da9413e..44f0dfe 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -20,11 +20,13 @@
 
 #include "KeyboardInputMapper.h"
 
+#include <ui/Rotation.h>
+
 namespace android {
 
 // --- Static Definitions ---
 
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+static int32_t rotateKeyCode(int32_t keyCode, ui::Rotation orientation) {
     static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
             // key codes enumerated counter-clockwise with the original (unrotated) key first
             // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
@@ -42,11 +44,10 @@
              AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
     };
 
-    LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
-    if (orientation != DISPLAY_ORIENTATION_0) {
+    if (orientation != ui::ROTATION_0) {
         for (const auto& rotation : KEYCODE_ROTATION_MAP) {
-            if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
-                return rotation[orientation];
+            if (rotation[static_cast<size_t>(ui::ROTATION_0)] == keyCode) {
+                return rotation[static_cast<size_t>(orientation)];
             }
         }
     }
@@ -100,11 +101,11 @@
     return mSource;
 }
 
-int32_t KeyboardInputMapper::getOrientation() {
+ui::Rotation KeyboardInputMapper::getOrientation() {
     if (mViewport) {
         return mViewport->orientation;
     }
-    return DISPLAY_ORIENTATION_0;
+    return ui::ROTATION_0;
 }
 
 int32_t KeyboardInputMapper::getDisplayId() {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 11d5ad2..0526fd8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -82,7 +82,7 @@
     void configureParameters();
     void dumpParameters(std::string& dump) const;
 
-    int32_t getOrientation();
+    ui::Rotation getOrientation();
     int32_t getDisplayId();
 
     [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index b193dff..633efc6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -16,10 +16,8 @@
 
 #include "../Macros.h"
 
-#include "MultiTouchInputMapper.h"
-#if defined(__ANDROID__)
 #include <android/sysprop/InputProperties.sysprop.h>
-#endif
+#include "MultiTouchInputMapper.h"
 
 namespace android {
 
@@ -218,12 +216,7 @@
 
 bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
     static const bool SIMULATE_STYLUS_WITH_TOUCH =
-#if defined(__ANDROID__)
             sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
-#else
-            // Disable this developer feature where sysproperties are not available
-            false;
-#endif
     return SIMULATE_STYLUS_WITH_TOUCH &&
             mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
 }
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 29a1bda..19a79d7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -25,7 +25,7 @@
 namespace android {
 
 RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) {
+      : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) {
     mSource = AINPUT_SOURCE_ROTARY_ENCODER;
 }
 
@@ -73,7 +73,7 @@
         if (internalViewport) {
             mOrientation = internalViewport->orientation;
         } else {
-            mOrientation = DISPLAY_ORIENTATION_0;
+            mOrientation = ui::ROTATION_0;
         }
     }
     return out;
@@ -97,35 +97,34 @@
 
 std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
     std::list<NotifyArgs> out;
-    PointerCoords pointerCoords;
-    pointerCoords.clear();
-
-    PointerProperties pointerProperties;
-    pointerProperties.clear();
-    pointerProperties.id = 0;
-    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
 
     float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
     bool scrolled = scroll != 0;
 
-    // This is not a pointer, so it's not associated with a display.
-    int32_t displayId = ADISPLAY_ID_NONE;
-
-    // Moving the rotary encoder should wake the device (if specified).
-    uint32_t policyFlags = 0;
-    if (scrolled && getDeviceContext().isExternal()) {
-        policyFlags |= POLICY_FLAG_WAKE;
-    }
-
-    if (mOrientation == DISPLAY_ORIENTATION_180) {
-        scroll = -scroll;
-    }
-
     // Send motion event.
     if (scrolled) {
         int32_t metaState = getContext()->getGlobalMetaState();
+        // This is not a pointer, so it's not associated with a display.
+        int32_t displayId = ADISPLAY_ID_NONE;
+
+        if (mOrientation == ui::ROTATION_180) {
+            scroll = -scroll;
+        }
+
+        PointerCoords pointerCoords;
+        pointerCoords.clear();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
+        PointerProperties pointerProperties;
+        pointerProperties.clear();
+        pointerProperties.id = 0;
+        pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+        uint32_t policyFlags = 0;
+        if (getDeviceContext().isExternal()) {
+            policyFlags |= POLICY_FLAG_WAKE;
+        }
+
         out.push_back(
                 NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                                  displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index f4352e7..cb5fd88 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <ui/Rotation.h>
+
 #include "CursorScrollAccumulator.h"
 #include "InputMapper.h"
 
@@ -40,7 +42,7 @@
 
     int32_t mSource;
     float mScalingFactor;
-    int32_t mOrientation;
+    ui::Rotation mOrientation;
 
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
new file mode 100644
index 0000000..c12e95d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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 <input/DisplayViewport.h>
+#include <stdint.h>
+#include <ui/Rotation.h>
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+namespace {
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+    std::list<NotifyArgs> out;
+    if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+         (currentButtonState & buttonState)) ||
+        (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+         !(currentButtonState & buttonState))) {
+        out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+                                    displayId, policyFlags, action, 0, keyCode, 0,
+                                    context->getGlobalMetaState(), when));
+    }
+    return out;
+}
+
+} // namespace
+
+ui::Rotation getInverseRotation(ui::Rotation orientation) {
+    switch (orientation) {
+        case ui::ROTATION_90:
+            return ui::ROTATION_270;
+        case ui::ROTATION_270:
+            return ui::ROTATION_90;
+        default:
+            return orientation;
+    }
+}
+
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
+    float temp;
+    switch (orientation) {
+        case ui::ROTATION_90:
+            temp = *deltaX;
+            *deltaX = *deltaY;
+            *deltaY = -temp;
+            break;
+
+        case ui::ROTATION_180:
+            *deltaX = -*deltaX;
+            *deltaY = -*deltaY;
+            break;
+
+        case ui::ROTATION_270:
+            temp = *deltaX;
+            *deltaX = -*deltaY;
+            *deltaY = temp;
+            break;
+
+        default:
+            break;
+    }
+}
+
+bool isPointerDown(int32_t buttonState) {
+    return buttonState &
+            (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+             AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState) {
+    std::list<NotifyArgs> out;
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+    return out;
+}
+
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+        const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+        nsecs_t lastEventTime) {
+    if (identifier.bus != BUS_BLUETOOTH) {
+        return {currentEventTime, readTime};
+    }
+
+    // Assume the fastest rate at which a Bluetooth touch device can report input events is one
+    // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
+    // will be separated by at least this amount.
+    constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+    // We define a maximum smoothing time delta so that we don't generate events too far into the
+    // future.
+    constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+    const nsecs_t smoothenedEventTime =
+            std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+                     currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+    // If we are modifying the event time, treat this event as a synthetically generated event for
+    // latency tracking purposes and use the event time as the read time (zero read latency).
+    const nsecs_t smoothenedReadTime =
+            smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+    return {smoothenedEventTime, smoothenedReadTime};
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index d8a4d34..3023e68 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -18,6 +18,7 @@
 
 #include <input/DisplayViewport.h>
 #include <stdint.h>
+#include <ui/Rotation.h>
 
 #include "EventHub.h"
 #include "InputListener.h"
@@ -25,81 +26,18 @@
 
 namespace android {
 
-// --- Static Definitions ---
+ui::Rotation getInverseRotation(ui::Rotation orientation);
 
-static int32_t getInverseRotation(int32_t orientation) {
-    switch (orientation) {
-        case DISPLAY_ORIENTATION_90:
-            return DISPLAY_ORIENTATION_270;
-        case DISPLAY_ORIENTATION_270:
-            return DISPLAY_ORIENTATION_90;
-        default:
-            return orientation;
-    }
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
-    float temp;
-    switch (orientation) {
-        case DISPLAY_ORIENTATION_90:
-            temp = *deltaX;
-            *deltaX = *deltaY;
-            *deltaY = -temp;
-            break;
-
-        case DISPLAY_ORIENTATION_180:
-            *deltaX = -*deltaX;
-            *deltaY = -*deltaY;
-            break;
-
-        case DISPLAY_ORIENTATION_270:
-            temp = *deltaX;
-            *deltaX = -*deltaY;
-            *deltaY = temp;
-            break;
-
-        default:
-            break;
-    }
-}
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY);
 
 // Returns true if the pointer should be reported as being down given the specified
 // button states.  This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
-    return buttonState &
-            (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
-             AMOTION_EVENT_BUTTON_TERTIARY);
-}
+bool isPointerDown(int32_t buttonState);
 
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
         InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
         int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
-        int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
-    std::list<NotifyArgs> out;
-    if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
-         (currentButtonState & buttonState)) ||
-        (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
-         !(currentButtonState & buttonState))) {
-        out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
-                                    displayId, policyFlags, action, 0, keyCode, 0,
-                                    context->getGlobalMetaState(), when));
-    }
-    return out;
-}
-
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
-        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
-        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
-        int32_t lastButtonState, int32_t currentButtonState) {
-    std::list<NotifyArgs> out;
-    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
-                               policyFlags, lastButtonState, currentButtonState,
-                               AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
-    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
-                               policyFlags, lastButtonState, currentButtonState,
-                               AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
-    return out;
-}
+        int32_t lastButtonState, int32_t currentButtonState);
 
 // For devices connected over Bluetooth, although they may produce events at a consistent rate,
 // the events might end up reaching Android in a "batched" manner through the Bluetooth
@@ -110,28 +48,8 @@
 // coordinates result in extremely large instantaneous velocities, which can negatively impact
 // user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
 // differ by at least a minimum delta value.
-static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
         const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
-        nsecs_t lastEventTime) {
-    if (identifier.bus != BUS_BLUETOOTH) {
-        return {currentEventTime, readTime};
-    }
-
-    // Assume the fastest rate at which a Bluetooth touch device can report input events is one
-    // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
-    // will be separated by at least this amount.
-    constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
-    // We define a maximum smoothing time delta so that we don't generate events too far into the
-    // future.
-    constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
-    const nsecs_t smoothenedEventTime =
-            std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
-                     currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
-    // If we are modifying the event time, treat this event as a synthetically generated event for
-    // latency tracking purposes and use the event time as the read time (zero read latency).
-    const nsecs_t smoothenedReadTime =
-            smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
-    return {smoothenedEventTime, smoothenedReadTime};
-}
+        nsecs_t lastEventTime);
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5be694f..cefc44e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -27,6 +27,7 @@
 #include "CursorScrollAccumulator.h"
 #include "TouchButtonAccumulator.h"
 #include "TouchCursorInputMapperCommon.h"
+#include "ui/Rotation.h"
 
 namespace android {
 
@@ -42,6 +43,19 @@
 
 static const DisplayViewport kUninitializedViewport;
 
+static std::string toString(const Rect& rect) {
+    return base::StringPrintf("Rect{%d, %d, %d, %d}", rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static std::string toString(const ui::Size& size) {
+    return base::StringPrintf("%dx%d", size.width, size.height);
+}
+
+static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) {
+    // Consider all four sides as "inclusive".
+    return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
+}
+
 template <typename T>
 inline static void swap(T& a, T& b) {
     T temp = a;
@@ -67,6 +81,31 @@
     return value >= 8 ? value - 16 : value;
 }
 
+static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
+        const DisplayViewport& viewport, ui::Rotation naturalOrientation) {
+    ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
+    if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) {
+        std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
+    }
+
+    ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation),
+                         rotatedDisplaySize.width, rotatedDisplaySize.height);
+
+    Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
+                       viewport.physicalBottom};
+    physicalFrame = rotate.transform(physicalFrame);
+
+    LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid());
+    if (physicalFrame.isEmpty()) {
+        ALOGE("Viewport is not set properly: %s", viewport.toString().c_str());
+        physicalFrame.right =
+                physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width());
+        physicalFrame.bottom =
+                physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height());
+    }
+    return {rotatedDisplaySize, physicalFrame};
+}
+
 // --- RawPointerData ---
 
 void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
@@ -93,13 +132,7 @@
         mTouchButtonAccumulator(deviceContext),
         mSource(0),
         mDeviceMode(DeviceMode::DISABLED),
-        mDisplayWidth(-1),
-        mDisplayHeight(-1),
-        mPhysicalWidth(-1),
-        mPhysicalHeight(-1),
-        mPhysicalLeft(0),
-        mPhysicalTop(0),
-        mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
+        mInputDeviceOrientation(ui::ROTATION_0) {}
 
 TouchInputMapper::~TouchInputMapper() {}
 
@@ -390,18 +423,18 @@
     getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
                                                          mParameters.orientationAware);
 
-    mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+    mParameters.orientation = ui::ROTATION_0;
     std::string orientationString;
     if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
                                                              orientationString)) {
         if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
             ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
         } else if (orientationString == "ORIENTATION_90") {
-            mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+            mParameters.orientation = ui::ROTATION_90;
         } else if (orientationString == "ORIENTATION_180") {
-            mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+            mParameters.orientation = ui::ROTATION_180;
         } else if (orientationString == "ORIENTATION_270") {
-            mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+            mParameters.orientation = ui::ROTATION_270;
         } else if (orientationString != "ORIENTATION_0") {
             ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
         }
@@ -564,7 +597,7 @@
     }
 
     // Size of diagonal axis.
-    const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
+    const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
 
     // Size factors.
     if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
@@ -647,8 +680,8 @@
 
 void TouchInputMapper::initializeOrientedRanges() {
     // Configure X and Y factors.
-    mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth();
-    mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight();
+    mXScale = float(mDisplayBounds.width) / mRawPointerAxes.getRawWidth();
+    mYScale = float(mDisplayBounds.height) / mRawPointerAxes.getRawHeight();
     mXPrecision = 1.0f / mXScale;
     mYPrecision = 1.0f / mYScale;
 
@@ -778,19 +811,19 @@
     // Note that the maximum value reported is an inclusive maximum value so it is one
     // unit less than the total width or height of the display.
     switch (mInputDeviceOrientation) {
-        case DISPLAY_ORIENTATION_90:
-        case DISPLAY_ORIENTATION_270:
+        case ui::ROTATION_90:
+        case ui::ROTATION_270:
             mOrientedXPrecision = mYPrecision;
             mOrientedYPrecision = mXPrecision;
 
             mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = mDisplayHeight - 1;
+            mOrientedRanges.x.max = mDisplayBounds.height - 1;
             mOrientedRanges.x.flat = 0;
             mOrientedRanges.x.fuzz = 0;
             mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
 
             mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = mDisplayWidth - 1;
+            mOrientedRanges.y.max = mDisplayBounds.width - 1;
             mOrientedRanges.y.flat = 0;
             mOrientedRanges.y.fuzz = 0;
             mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -801,13 +834,13 @@
             mOrientedYPrecision = mYPrecision;
 
             mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = mDisplayWidth - 1;
+            mOrientedRanges.x.max = mDisplayBounds.width - 1;
             mOrientedRanges.x.flat = 0;
             mOrientedRanges.x.fuzz = 0;
             mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
 
             mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = mDisplayHeight - 1;
+            mOrientedRanges.y.max = mDisplayBounds.height - 1;
             mOrientedRanges.y.flat = 0;
             mOrientedRanges.y.fuzz = 0;
             mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -868,8 +901,7 @@
     }
 
     // Raw width and height in the natural orientation.
-    const int32_t rawWidth = mRawPointerAxes.getRawWidth();
-    const int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
     const int32_t rawXResolution = mRawPointerAxes.x.resolution;
     const int32_t rawYResolution = mRawPointerAxes.y.resolution;
     // Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
@@ -885,67 +917,16 @@
         mViewport = newViewport;
 
         if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
-            // Convert rotated viewport to the natural orientation.
-            int32_t naturalPhysicalWidth, naturalPhysicalHeight;
-            int32_t naturalPhysicalLeft, naturalPhysicalTop;
-            int32_t naturalDeviceWidth, naturalDeviceHeight;
+            const auto oldDisplayBounds = mDisplayBounds;
 
             // Apply the inverse of the input device orientation so that the input device is
             // configured in the same orientation as the viewport. The input device orientation will
             // be re-applied by mInputDeviceOrientation.
-            const int32_t naturalDeviceOrientation =
-                    (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
-            switch (naturalDeviceOrientation) {
-                case DISPLAY_ORIENTATION_90:
-                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
-                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
-                    naturalPhysicalTop = mViewport.physicalLeft;
-                    naturalDeviceWidth = mViewport.deviceHeight;
-                    naturalDeviceHeight = mViewport.deviceWidth;
-                    break;
-                case DISPLAY_ORIENTATION_180:
-                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
-                    naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
-                    naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
-                    naturalDeviceWidth = mViewport.deviceWidth;
-                    naturalDeviceHeight = mViewport.deviceHeight;
-                    break;
-                case DISPLAY_ORIENTATION_270:
-                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
-                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalLeft = mViewport.physicalTop;
-                    naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
-                    naturalDeviceWidth = mViewport.deviceHeight;
-                    naturalDeviceHeight = mViewport.deviceWidth;
-                    break;
-                case DISPLAY_ORIENTATION_0:
-                default:
-                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
-                    naturalPhysicalLeft = mViewport.physicalLeft;
-                    naturalPhysicalTop = mViewport.physicalTop;
-                    naturalDeviceWidth = mViewport.deviceWidth;
-                    naturalDeviceHeight = mViewport.deviceHeight;
-                    break;
-            }
+            const ui::Rotation naturalDeviceOrientation =
+                    mViewport.orientation - mParameters.orientation;
 
-            if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
-                ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
-                naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
-                naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
-            }
-
-            mPhysicalWidth = naturalPhysicalWidth;
-            mPhysicalHeight = naturalPhysicalHeight;
-            mPhysicalLeft = naturalPhysicalLeft;
-            mPhysicalTop = naturalPhysicalTop;
-
-            const int32_t oldDisplayWidth = mDisplayWidth;
-            const int32_t oldDisplayHeight = mDisplayHeight;
-            mDisplayWidth = naturalDeviceWidth;
-            mDisplayHeight = naturalDeviceHeight;
+            std::tie(mDisplayBounds, mPhysicalFrameInDisplay) =
+                    getNaturalDisplayInfo(mViewport, naturalDeviceOrientation);
 
             // InputReader works in the un-rotated display coordinate space, so we don't need to do
             // anything if the device is already orientation-aware. If the device is not
@@ -953,26 +934,19 @@
             // when the display rotation is applied later as a part of the per-window transform, we
             // get the expected screen coordinates.
             mInputDeviceOrientation = mParameters.orientationAware
-                    ? DISPLAY_ORIENTATION_0
+                    ? ui::ROTATION_0
                     : getInverseRotation(mViewport.orientation);
             // For orientation-aware devices that work in the un-rotated coordinate space, the
             // viewport update should be skipped if it is only a change in the orientation.
             skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
-                    mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight &&
-                    viewportOrientationChanged;
+                    mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;
 
             // Apply the input device orientation for the device.
-            mInputDeviceOrientation =
-                    (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+            mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
         } else {
-            mPhysicalWidth = rawWidth;
-            mPhysicalHeight = rawHeight;
-            mPhysicalLeft = 0;
-            mPhysicalTop = 0;
-
-            mDisplayWidth = rawWidth;
-            mDisplayHeight = rawHeight;
-            mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
+            mDisplayBounds = rawSize;
+            mPhysicalFrameInDisplay = Rect{mDisplayBounds};
+            mInputDeviceOrientation = ui::ROTATION_0;
         }
     }
 
@@ -1003,9 +977,9 @@
     }
 
     if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
-        ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+        ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
               "display id %d",
-              getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+              getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
               mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
 
         configureVirtualKeys();
@@ -1017,8 +991,8 @@
 
         if (mDeviceMode == DeviceMode::POINTER) {
             // Compute pointer gesture detection parameters.
-            float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
+            float rawDiagonal = hypotf(rawSize.width, rawSize.height);
+            float displayDiagonal = hypotf(mDisplayBounds.width, mDisplayBounds.height);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
@@ -1054,12 +1028,8 @@
 
 void TouchInputMapper::dumpDisplay(std::string& dump) {
     dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
-    dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
-    dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
-    dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
-    dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
-    dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
-    dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
+    dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str());
+    dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str());
     dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
 }
 
@@ -1098,17 +1068,17 @@
         int32_t halfWidth = virtualKeyDefinition.width / 2;
         int32_t halfHeight = virtualKeyDefinition.height / 2;
 
-        virtualKey.hitLeft =
-                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
+        virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth /
+                        mDisplayBounds.width +
                 touchScreenLeft;
-        virtualKey.hitRight =
-                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
+        virtualKey.hitRight = (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth /
+                        mDisplayBounds.width +
                 touchScreenLeft;
-        virtualKey.hitTop =
-                (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
+        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+                        mDisplayBounds.height +
                 touchScreenTop;
-        virtualKey.hitBottom =
-                (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
+        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+                        mDisplayBounds.height +
                 touchScreenTop;
         mVirtualKeys.push_back(virtualKey);
     }
@@ -2377,7 +2347,7 @@
         float left, top, right, bottom;
 
         switch (mInputDeviceOrientation) {
-            case DISPLAY_ORIENTATION_90:
+            case ui::ROTATION_90:
                 left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
                 right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
                 bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
@@ -2388,7 +2358,7 @@
                             (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
                 }
                 break;
-            case DISPLAY_ORIENTATION_180:
+            case ui::ROTATION_180:
                 left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
                 bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
@@ -2399,7 +2369,7 @@
                             (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
                 }
                 break;
-            case DISPLAY_ORIENTATION_270:
+            case ui::ROTATION_270:
                 left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
                 bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
@@ -3833,19 +3803,19 @@
     // 180 - reverse x, y.
     // 270 - swap x/y and reverse x.
     switch (mInputDeviceOrientation) {
-        case DISPLAY_ORIENTATION_0:
+        case ui::ROTATION_0:
             x = xScaled;
             y = yScaled;
             break;
-        case DISPLAY_ORIENTATION_90:
+        case ui::ROTATION_90:
             y = xScaledMax;
             x = yScaled;
             break;
-        case DISPLAY_ORIENTATION_180:
+        case ui::ROTATION_180:
             x = xScaledMax;
             y = yScaledMax;
             break;
-        case DISPLAY_ORIENTATION_270:
+        case ui::ROTATION_270:
             y = xScaled;
             x = yScaledMax;
             break;
@@ -3859,9 +3829,8 @@
     const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
 
     return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
-            xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
             y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
-            yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
+            isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled);
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 788ec58..34ba625 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <ui/Rotation.h>
 
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
@@ -218,15 +219,7 @@
         bool associatedDisplayIsExternal;
         bool orientationAware;
 
-        enum class Orientation : int32_t {
-            ORIENTATION_0 = DISPLAY_ORIENTATION_0,
-            ORIENTATION_90 = DISPLAY_ORIENTATION_90,
-            ORIENTATION_180 = DISPLAY_ORIENTATION_180,
-            ORIENTATION_270 = DISPLAY_ORIENTATION_270,
-
-            ftl_last = ORIENTATION_270
-        };
-        Orientation orientation;
+        ui::Rotation orientation;
 
         bool hasButtonUnderPad;
         std::string uniqueDisplayId;
@@ -412,22 +405,19 @@
     // The components of the viewport are specified in the display's rotated orientation.
     DisplayViewport mViewport;
 
-    // The width and height are obtained from the viewport and are specified
-    // in the natural orientation.
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
+    // We refer to the display as being in the "natural orientation" when there is no rotation
+    // applied. The display size obtained from the viewport in the natural orientation.
+    // Always starts at (0, 0).
+    ui::Size mDisplayBounds{ui::kInvalidSize};
 
-    // The physical frame is the rectangle in the display's coordinate space that maps to the
+    // The physical frame is the rectangle in the natural display's coordinate space that maps to
     // the logical display frame.
-    int32_t mPhysicalWidth;
-    int32_t mPhysicalHeight;
-    int32_t mPhysicalLeft;
-    int32_t mPhysicalTop;
+    Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT};
 
     // The orientation of the input device relative to that of the display panel. It specifies
     // the rotation of the input device coordinates required to produce the display panel
     // orientation, so it will depend on whether the device is orientation aware.
-    int32_t mInputDeviceOrientation;
+    ui::Rotation mInputDeviceOrientation;
 
     // Translation and scaling factors, orientation-independent.
     float mXScale;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
new file mode 100644
index 0000000..de6e4b0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2022 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 "../Macros.h"
+
+#include <chrono>
+
+#include <android/input.h>
+#include <log/log_main.h>
+#include "TouchCursorInputMapperCommon.h"
+#include "TouchpadInputMapper.h"
+
+namespace android {
+
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+    if (context.hasKeyCode(BTN_TOOL_QUINTTAP)) return 5;
+    if (context.hasKeyCode(BTN_TOOL_QUADTAP)) return 4;
+    if (context.hasKeyCode(BTN_TOOL_TRIPLETAP)) return 3;
+    if (context.hasKeyCode(BTN_TOOL_DOUBLETAP)) return 2;
+    if (context.hasKeyCode(BTN_TOOL_FINGER)) return 1;
+    return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+    HardwareProperties props;
+    RawAbsoluteAxisInfo absMtPositionX;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+    props.left = absMtPositionX.minValue;
+    props.right = absMtPositionX.maxValue;
+    props.res_x = absMtPositionX.resolution;
+
+    RawAbsoluteAxisInfo absMtPositionY;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+    props.top = absMtPositionY.minValue;
+    props.bottom = absMtPositionY.maxValue;
+    props.res_y = absMtPositionY.resolution;
+
+    RawAbsoluteAxisInfo absMtOrientation;
+    context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+    props.orientation_minimum = absMtOrientation.minValue;
+    props.orientation_maximum = absMtOrientation.maxValue;
+
+    RawAbsoluteAxisInfo absMtSlot;
+    context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+    props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+    props.max_touch_cnt = getMaxTouchCount(context);
+
+    // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+    // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+    // that did this, so assume false.
+    props.supports_t5r2 = false;
+
+    props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+    props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+    // Mouse-only properties, which will always be false.
+    props.has_wheel = false;
+    props.wheel_is_hi_res = false;
+
+    // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+    // are haptic.
+    props.is_haptic_pad = false;
+    return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
+    TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
+    mapper->consumeGesture(gesture);
+}
+
+uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
+    switch (gesturesButton) {
+        case GESTURES_BUTTON_LEFT:
+            return AMOTION_EVENT_BUTTON_PRIMARY;
+        case GESTURES_BUTTON_MIDDLE:
+            return AMOTION_EVENT_BUTTON_TERTIARY;
+        case GESTURES_BUTTON_RIGHT:
+            return AMOTION_EVENT_BUTTON_SECONDARY;
+        case GESTURES_BUTTON_BACK:
+            return AMOTION_EVENT_BUTTON_BACK;
+        case GESTURES_BUTTON_FORWARD:
+            return AMOTION_EVENT_BUTTON_FORWARD;
+        default:
+            return 0;
+    }
+}
+
+} // namespace
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext),
+        mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+        mPointerController(getContext()->getPointerController(getDeviceId())),
+        mTouchButtonAccumulator(deviceContext) {
+    mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+    mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+    // Even though we don't explicitly delete copy/move semantics, it's safe to
+    // give away a pointer to TouchpadInputMapper here because
+    // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
+    // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+    mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
+    // TODO(b/251196347): set a property provider, so we can change gesture properties.
+    // TODO(b/251196347): set a timer provider, so the library can use timers.
+
+    RawAbsoluteAxisInfo slotAxisInfo;
+    getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+    if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+        ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+              "properly.",
+              getDeviceName().c_str());
+    }
+    mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true);
+    mTouchButtonAccumulator.configure();
+}
+
+TouchpadInputMapper::~TouchpadInputMapper() {
+    if (mPointerController != nullptr) {
+        mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+    }
+}
+
+uint32_t TouchpadInputMapper::getSources() const {
+    return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+    mCursorButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
+    mMscTimestamp = 0;
+
+    mButtonState = 0;
+    return InputMapper::reset(when);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out = {};
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        out = sync(rawEvent->when, rawEvent->readTime);
+    }
+    if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+        mMscTimestamp = rawEvent->value;
+    }
+    mCursorButtonAccumulator.process(rawEvent);
+    mMotionAccumulator.process(rawEvent);
+    mTouchButtonAccumulator.process(rawEvent);
+    return out;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    HardwareState hwState;
+    // The gestures library uses doubles to represent timestamps in seconds.
+    hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+    hwState.msc_timestamp =
+            std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+    hwState.buttons_down = 0;
+    if (mCursorButtonAccumulator.isLeftPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_LEFT;
+    }
+    if (mCursorButtonAccumulator.isMiddlePressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_MIDDLE;
+    }
+    if (mCursorButtonAccumulator.isRightPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_RIGHT;
+    }
+    if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_BACK;
+    }
+    if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_FORWARD;
+    }
+
+    std::vector<FingerState> fingers;
+    for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+        MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+        if (slot.isInUse()) {
+            FingerState& fingerState = fingers.emplace_back();
+            fingerState = {};
+            fingerState.touch_major = slot.getTouchMajor();
+            fingerState.touch_minor = slot.getTouchMinor();
+            fingerState.width_major = slot.getToolMajor();
+            fingerState.width_minor = slot.getToolMinor();
+            fingerState.pressure = slot.getPressure();
+            fingerState.orientation = slot.getOrientation();
+            fingerState.position_x = slot.getX();
+            fingerState.position_y = slot.getY();
+            fingerState.tracking_id = slot.getTrackingId();
+        }
+    }
+    hwState.fingers = fingers.data();
+    hwState.finger_cnt = fingers.size();
+    hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+
+    mProcessing = true;
+    mGestureInterpreter->PushHardwareState(&hwState);
+    mProcessing = false;
+
+    std::list<NotifyArgs> out = processGestures(when, readTime);
+
+    mMotionAccumulator.finishSync();
+    mMscTimestamp = 0;
+    return out;
+}
+
+void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+    ALOGD("Gesture ready: %s", gesture->String().c_str());
+    if (!mProcessing) {
+        ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
+        return;
+    }
+    mGesturesToProcess.push_back(*gesture);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out = {};
+    for (Gesture& gesture : mGesturesToProcess) {
+        switch (gesture.type) {
+            case kGestureTypeMove:
+                out.push_back(handleMove(when, readTime, gesture));
+                break;
+            case kGestureTypeButtonsChange:
+                out += handleButtonsChange(when, readTime, gesture);
+                break;
+            default:
+                // TODO(b/251196347): handle more gesture types.
+                break;
+        }
+    }
+    mGesturesToProcess.clear();
+    return out;
+}
+
+NotifyArgs TouchpadInputMapper::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+    PointerProperties props;
+    props.clear();
+    props.id = 0;
+    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+    mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+    mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+    float xCursorPosition, yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+    PointerCoords coords;
+    coords.clear();
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+    const bool down = isPointerDown(mButtonState);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+    const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
+    return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
+                          /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::handleButtonsChange(nsecs_t when, nsecs_t readTime,
+                                                               const Gesture& gesture) {
+    std::list<NotifyArgs> out = {};
+
+    mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+    mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+    PointerProperties props;
+    props.clear();
+    props.id = 0;
+    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    float xCursorPosition, yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+    PointerCoords coords;
+    coords.clear();
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+    const uint32_t buttonsPressed = gesture.details.buttons.down;
+    bool pointerDown = isPointerDown(mButtonState) ||
+            buttonsPressed &
+                    (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+
+    uint32_t newButtonState = mButtonState;
+    std::list<NotifyArgs> pressEvents = {};
+    for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+        if (buttonsPressed & button) {
+            uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+            newButtonState |= actionButton;
+            pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                                                 actionButton, newButtonState,
+                                                 /* pointerCount= */ 1, &props, &coords,
+                                                 xCursorPosition, yCursorPosition));
+        }
+    }
+    if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
+        mDownTime = when;
+        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+                                     /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
+                                     &props, &coords, xCursorPosition, yCursorPosition));
+    }
+    out.splice(out.end(), pressEvents);
+
+    // The same button may be in both down and up in the same gesture, in which case we should treat
+    // it as having gone down and then up. So, we treat a single button change gesture as two state
+    // changes: a set of buttons going down, followed by a set of buttons going up.
+    mButtonState = newButtonState;
+
+    const uint32_t buttonsReleased = gesture.details.buttons.up;
+    for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+        if (buttonsReleased & button) {
+            uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+            newButtonState &= ~actionButton;
+            out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                                         actionButton, newButtonState, /* pointerCount= */ 1,
+                                         &props, &coords, xCursorPosition, yCursorPosition));
+        }
+    }
+    if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
+        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+                                     newButtonState, /* pointerCount= */ 1, &props, &coords,
+                                     xCursorPosition, yCursorPosition));
+    }
+    mButtonState = newButtonState;
+    return out;
+}
+
+NotifyMotionArgs TouchpadInputMapper::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+                                                     int32_t actionButton, int32_t buttonState,
+                                                     uint32_t pointerCount,
+                                                     const PointerProperties* pointerProperties,
+                                                     const PointerCoords* pointerCoords,
+                                                     float xCursorPosition, float yCursorPosition) {
+    // TODO(b/260226362): consider what the appropriate source for these events is.
+    const uint32_t source = AINPUT_SOURCE_MOUSE;
+
+    return NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), source,
+                            mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
+                            /* actionButton= */ actionButton, /* flags= */ 0,
+                            getContext()->getGlobalMetaState(), buttonState,
+                            MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+                            pointerProperties, pointerCoords,
+                            /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
+                            yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
new file mode 100644
index 0000000..fe6b1fe
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 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 <memory>
+
+#include <PointerControllerInterface.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputMapper.h"
+#include "NotifyArgs.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+class TouchpadInputMapper : public InputMapper {
+public:
+    explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+    ~TouchpadInputMapper();
+
+    uint32_t getSources() const override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+    void consumeGesture(const Gesture* gesture);
+
+private:
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
+    NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+    [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
+                                                            const Gesture& gesture);
+
+    NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+                                    int32_t actionButton, int32_t buttonState,
+                                    uint32_t pointerCount,
+                                    const PointerProperties* pointerProperties,
+                                    const PointerCoords* pointerCoords, float xCursorPosition,
+                                    float yCursorPosition);
+
+    std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+            mGestureInterpreter;
+    std::shared_ptr<PointerControllerInterface> mPointerController;
+
+    CursorButtonAccumulator mCursorButtonAccumulator;
+    MultiTouchMotionAccumulator mMotionAccumulator;
+    TouchButtonAccumulator mTouchButtonAccumulator;
+    int32_t mMscTimestamp = 0;
+
+    bool mProcessing = false;
+    std::vector<Gesture> mGesturesToProcess;
+
+    // The current button state according to the gestures library, but converted into MotionEvent
+    // button values (AMOTION_EVENT_BUTTON_...).
+    uint32_t mButtonState = 0;
+    nsecs_t mDownTime = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..1380604 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -32,6 +32,14 @@
     void process(const RawEvent* rawEvent);
 
     uint32_t getButtonState() const;
+    inline bool isLeftPressed() const { return mBtnLeft; }
+    inline bool isRightPressed() const { return mBtnRight; }
+    inline bool isMiddlePressed() const { return mBtnMiddle; }
+    inline bool isBackPressed() const { return mBtnBack; }
+    inline bool isSidePressed() const { return mBtnSide; }
+    inline bool isForwardPressed() const { return mBtnForward; }
+    inline bool isExtraPressed() const { return mBtnExtra; }
+    inline bool isTaskPressed() const { return mBtnTask; }
 
 private:
     bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index bc23a8e..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -48,6 +48,7 @@
     mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
     mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
     mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+    mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
     mHidUsageAccumulator.reset();
 }
 
@@ -100,6 +101,9 @@
             case BTN_TOOL_QUADTAP:
                 mBtnToolQuadTap = rawEvent->value;
                 break;
+            case BTN_TOOL_QUINTTAP:
+                mBtnToolQuintTap = rawEvent->value;
+                break;
             default:
                 processMappedKey(rawEvent->code, rawEvent->value);
         }
@@ -147,7 +151,8 @@
     if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
         return AMOTION_EVENT_TOOL_TYPE_STYLUS;
     }
-    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+        mBtnToolQuintTap) {
         return AMOTION_EVENT_TOOL_TYPE_FINGER;
     }
     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -156,7 +161,7 @@
 bool TouchButtonAccumulator::isToolActive() const {
     return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
             mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
-            mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+            mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
 }
 
 bool TouchButtonAccumulator::isHovering() const {
@@ -171,4 +176,15 @@
     return mHaveBtnTouch;
 }
 
+int TouchButtonAccumulator::getTouchCount() const {
+    if (mBtnTouch) {
+        if (mBtnToolQuintTap) return 5;
+        if (mBtnToolQuadTap) return 4;
+        if (mBtnToolTripleTap) return 3;
+        if (mBtnToolDoubleTap) return 2;
+        if (mBtnToolFinger) return 1;
+    }
+    return 0;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c2de23c..2e70e2e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -41,6 +41,7 @@
     bool isHovering() const;
     bool hasStylus() const;
     bool hasButtonTouch() const;
+    int getTouchCount() const;
 
 private:
     bool mHaveBtnTouch{};
@@ -60,6 +61,7 @@
     bool mBtnToolDoubleTap{};
     bool mBtnToolTripleTap{};
     bool mBtnToolQuadTap{};
+    bool mBtnToolQuintTap{};
 
     HidUsageAccumulator mHidUsageAccumulator{};
 
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
new file mode 100644
index 0000000..81b4968
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG "Gestures"
+#endif
+
+#include <stdio.h>
+
+#include <log/log.h>
+
+#include "include/gestures.h"
+
+extern "C" {
+
+void gestures_log(int verb, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    if (verb == GESTURES_LOG_ERROR) {
+        LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
+    } else if (verb == GESTURES_LOG_INFO) {
+        LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+    } else {
+        LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+    }
+    va_end(args);
+}
+}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index e55e121..53d821f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,12 +40,18 @@
         "AnrTracker_test.cpp",
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
+        "FakeEventHub.cpp",
+        "FakeInputReaderPolicy.cpp",
+        "FakePointerController.cpp",
         "FocusResolver_test.cpp",
+        "InputMapperTest.cpp",
         "InputProcessor_test.cpp",
         "InputProcessorConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
+        "InstrumentedInputReader.cpp",
         "LatencyTracker_test.cpp",
+        "NotifyArgs_test.cpp",
         "PreferStylusOverTouch_test.cpp",
         "TestInputListener.cpp",
         "UinputDevice.cpp",
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
new file mode 100644
index 0000000..289a780
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2022 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 "FakeEventHub.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "TestConstants.h"
+
+namespace android {
+
+const std::string FakeEventHub::BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
+
+FakeEventHub::~FakeEventHub() {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        delete mDevices.valueAt(i);
+    }
+}
+
+void FakeEventHub::addDevice(int32_t deviceId, const std::string& name,
+                             ftl::Flags<InputDeviceClass> classes, int bus) {
+    Device* device = new Device(classes);
+    device->identifier.name = name;
+    device->identifier.bus = bus;
+    mDevices.add(deviceId, device);
+
+    enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+}
+
+void FakeEventHub::removeDevice(int32_t deviceId) {
+    delete mDevices.valueFor(deviceId);
+    mDevices.removeItem(deviceId);
+
+    enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+}
+
+bool FakeEventHub::isDeviceEnabled(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+        return false;
+    }
+    return device->enabled;
+}
+
+status_t FakeEventHub::enableDevice(int32_t deviceId) {
+    status_t result;
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+        return BAD_VALUE;
+    }
+    if (device->enabled) {
+        ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
+        return OK;
+    }
+    result = device->enable();
+    return result;
+}
+
+status_t FakeEventHub::disableDevice(int32_t deviceId) {
+    Device* device = getDevice(deviceId);
+    if (device == nullptr) {
+        ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+        return BAD_VALUE;
+    }
+    if (!device->enabled) {
+        ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
+        return OK;
+    }
+    return device->disable();
+}
+
+void FakeEventHub::finishDeviceScan() {
+    enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+}
+
+void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
+    getDevice(deviceId)->configuration.addProperty(key, value);
+}
+
+void FakeEventHub::addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
+    getDevice(deviceId)->configuration.addAll(configuration);
+}
+
+void FakeEventHub::addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue,
+                                   int flat, int fuzz, int resolution) {
+    Device* device = getDevice(deviceId);
+
+    RawAbsoluteAxisInfo info;
+    info.valid = true;
+    info.minValue = minValue;
+    info.maxValue = maxValue;
+    info.flat = flat;
+    info.fuzz = fuzz;
+    info.resolution = resolution;
+    device->absoluteAxes.add(axis, info);
+}
+
+void FakeEventHub::addRelativeAxis(int32_t deviceId, int32_t axis) {
+    getDevice(deviceId)->relativeAxes.add(axis, true);
+}
+
+void FakeEventHub::setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
+    getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state);
+}
+
+void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
+    getDevice(deviceId)->countryCode = countryCode;
+}
+
+void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
+    getDevice(deviceId)->scanCodeStates.replaceValueFor(scanCode, state);
+}
+
+void FakeEventHub::setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
+    getDevice(deviceId)->switchStates.replaceValueFor(switchCode, state);
+}
+
+void FakeEventHub::setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+    getDevice(deviceId)->absoluteAxisValue.replaceValueFor(axis, value);
+}
+
+void FakeEventHub::addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+                          uint32_t flags) {
+    Device* device = getDevice(deviceId);
+    KeyInfo info;
+    info.keyCode = keyCode;
+    info.flags = flags;
+    if (scanCode) {
+        device->keysByScanCode.add(scanCode, info);
+    }
+    if (usageCode) {
+        device->keysByUsageCode.add(usageCode, info);
+    }
+}
+
+void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
+    getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+    Device* device = getDevice(deviceId);
+    device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
+    getDevice(deviceId)->leds.add(led, initialState);
+}
+
+void FakeEventHub::addSensorAxis(int32_t deviceId, int32_t absCode,
+                                 InputDeviceSensorType sensorType, int32_t sensorDataIndex) {
+    SensorInfo info;
+    info.sensorType = sensorType;
+    info.sensorDataIndex = sensorDataIndex;
+    getDevice(deviceId)->sensorsByAbsCode.emplace(absCode, info);
+}
+
+void FakeEventHub::setMscEvent(int32_t deviceId, int32_t mscEvent) {
+    typename BitArray<MSC_MAX>::Buffer buffer;
+    buffer[mscEvent / 32] = 1 << mscEvent % 32;
+    getDevice(deviceId)->mscBitmask.loadFromBuffer(buffer);
+}
+
+void FakeEventHub::addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
+    mRawLightInfos.emplace(rawId, std::move(info));
+}
+
+void FakeEventHub::fakeLightBrightness(int32_t rawId, int32_t brightness) {
+    mLightBrightness.emplace(rawId, brightness);
+}
+
+void FakeEventHub::fakeLightIntensities(int32_t rawId,
+                                        const std::unordered_map<LightColor, int32_t> intensities) {
+    mLightIntensities.emplace(rawId, std::move(intensities));
+}
+
+bool FakeEventHub::getLedState(int32_t deviceId, int32_t led) {
+    return getDevice(deviceId)->leds.valueFor(led);
+}
+
+std::vector<std::string>& FakeEventHub::getExcludedDevices() {
+    return mExcludedDevices;
+}
+
+void FakeEventHub::addVirtualKeyDefinition(int32_t deviceId,
+                                           const VirtualKeyDefinition& definition) {
+    getDevice(deviceId)->virtualKeys.push_back(definition);
+}
+
+void FakeEventHub::enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type,
+                                int32_t code, int32_t value) {
+    std::scoped_lock<std::mutex> lock(mLock);
+    RawEvent event;
+    event.when = when;
+    event.readTime = readTime;
+    event.deviceId = deviceId;
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    mEvents.push_back(event);
+
+    if (type == EV_ABS) {
+        setAbsoluteAxisValue(deviceId, code, value);
+    }
+}
+
+void FakeEventHub::setVideoFrames(
+        std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) {
+    mVideoFrames = std::move(videoFrames);
+}
+
+void FakeEventHub::assertQueueIsEmpty() {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+    const bool queueIsEmpty =
+            mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+                                      [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+    if (!queueIsEmpty) {
+        FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+    }
+}
+
+FakeEventHub::Device* FakeEventHub::getDevice(int32_t deviceId) const {
+    ssize_t index = mDevices.indexOfKey(deviceId);
+    return index >= 0 ? mDevices.valueAt(index) : nullptr;
+}
+
+ftl::Flags<InputDeviceClass> FakeEventHub::getDeviceClasses(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
+}
+
+InputDeviceIdentifier FakeEventHub::getDeviceIdentifier(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    return device ? device->identifier : InputDeviceIdentifier();
+}
+
+int32_t FakeEventHub::getDeviceControllerNumber(int32_t) const {
+    return 0;
+}
+
+void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        *outConfiguration = device->configuration;
+    }
+}
+
+status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                           RawAbsoluteAxisInfo* outAxisInfo) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->absoluteAxes.indexOfKey(axis);
+        if (index >= 0) {
+            *outAxisInfo = device->absoluteAxes.valueAt(index);
+            return OK;
+        }
+    }
+    outAxisInfo->clear();
+    return -1;
+}
+
+bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        return device->relativeAxes.indexOfKey(axis) >= 0;
+    }
+    return false;
+}
+
+bool FakeEventHub::hasInputProperty(int32_t, int) const {
+    return false;
+}
+
+bool FakeEventHub::hasMscEvent(int32_t deviceId, int mscEvent) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+    }
+    return false;
+}
+
+status_t FakeEventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+                              int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+                              uint32_t* outFlags) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        const KeyInfo* key = getKey(device, scanCode, usageCode);
+        if (key) {
+            if (outKeycode) {
+                auto it = device->keyRemapping.find(key->keyCode);
+                *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode;
+            }
+            if (outFlags) {
+                *outFlags = key->flags;
+            }
+            if (outMetaState) {
+                *outMetaState = metaState;
+            }
+            return OK;
+        }
+    }
+    return NAME_NOT_FOUND;
+}
+
+const FakeEventHub::KeyInfo* FakeEventHub::getKey(Device* device, int32_t scanCode,
+                                                  int32_t usageCode) const {
+    if (usageCode) {
+        ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
+        if (index >= 0) {
+            return &device->keysByUsageCode.valueAt(index);
+        }
+    }
+    if (scanCode) {
+        ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+        if (index >= 0) {
+            return &device->keysByScanCode.valueAt(index);
+        }
+    }
+    return nullptr;
+}
+
+status_t FakeEventHub::mapAxis(int32_t, int32_t, AxisInfo*) const {
+    return NAME_NOT_FOUND;
+}
+
+base::Result<std::pair<InputDeviceSensorType, int32_t>> FakeEventHub::mapSensor(
+        int32_t deviceId, int32_t absCode) const {
+    Device* device = getDevice(deviceId);
+    if (!device) {
+        return Errorf("Sensor device not found.");
+    }
+    auto it = device->sensorsByAbsCode.find(absCode);
+    if (it == device->sensorsByAbsCode.end()) {
+        return Errorf("Sensor map not found.");
+    }
+    const SensorInfo& info = it->second;
+    return std::make_pair(info.sensorType, info.sensorDataIndex);
+}
+
+void FakeEventHub::setExcludedDevices(const std::vector<std::string>& devices) {
+    mExcludedDevices = devices;
+}
+
+std::vector<RawEvent> FakeEventHub::getEvents(int) {
+    std::scoped_lock lock(mLock);
+
+    std::vector<RawEvent> buffer;
+    std::swap(buffer, mEvents);
+
+    mEventsCondition.notify_all();
+    return buffer;
+}
+
+std::vector<TouchVideoFrame> FakeEventHub::getVideoFrames(int32_t deviceId) {
+    auto it = mVideoFrames.find(deviceId);
+    if (it != mVideoFrames.end()) {
+        std::vector<TouchVideoFrame> frames = std::move(it->second);
+        mVideoFrames.erase(deviceId);
+        return frames;
+    }
+    return {};
+}
+
+int32_t FakeEventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
+        if (index >= 0) {
+            return device->scanCodeStates.valueAt(index);
+        }
+    }
+    return AKEY_STATE_UNKNOWN;
+}
+
+InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const {
+    Device* device = getDevice(deviceId);
+    return device ? device->countryCode : InputDeviceCountryCode::INVALID;
+}
+
+int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
+        if (index >= 0) {
+            return device->keyCodeStates.valueAt(index);
+        }
+    }
+    return AKEY_STATE_UNKNOWN;
+}
+
+int32_t FakeEventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->switchStates.indexOfKey(sw);
+        if (index >= 0) {
+            return device->switchStates.valueAt(index);
+        }
+    }
+    return AKEY_STATE_UNKNOWN;
+}
+
+status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                            int32_t* outValue) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+        if (index >= 0) {
+            *outValue = device->absoluteAxisValue.valueAt(index);
+            return OK;
+        }
+    }
+    *outValue = 0;
+    return -1;
+}
+
+int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+    Device* device = getDevice(deviceId);
+    if (!device) {
+        return AKEYCODE_UNKNOWN;
+    }
+    auto it = device->keyCodeMapping.find(locationKeyCode);
+    return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
+}
+
+// Return true if the device has non-empty key layout.
+bool FakeEventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+                                         uint8_t* outFlags) const {
+    Device* device = getDevice(deviceId);
+    if (!device) return false;
+
+    bool result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
+    for (size_t i = 0; i < keyCodes.size(); i++) {
+        for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
+            if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
+                outFlags[i] = 1;
+            }
+        }
+        for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+            if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
+                outFlags[i] = 1;
+            }
+        }
+    }
+    return result;
+}
+
+bool FakeEventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+        return index >= 0;
+    }
+    return false;
+}
+
+bool FakeEventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const {
+    Device* device = getDevice(deviceId);
+    if (!device) {
+        return false;
+    }
+    for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
+        if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
+            return true;
+        }
+    }
+    for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+        if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool FakeEventHub::hasLed(int32_t deviceId, int32_t led) const {
+    Device* device = getDevice(deviceId);
+    return device && device->leds.indexOfKey(led) >= 0;
+}
+
+void FakeEventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+    Device* device = getDevice(deviceId);
+    if (device) {
+        ssize_t index = device->leds.indexOfKey(led);
+        if (index >= 0) {
+            device->leds.replaceValueAt(led, on);
+        } else {
+            ADD_FAILURE() << "Attempted to set the state of an LED that the EventHub declared "
+                             "was not present.  led="
+                          << led;
+        }
+    }
+}
+
+void FakeEventHub::getVirtualKeyDefinitions(
+        int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+    outVirtualKeys.clear();
+
+    Device* device = getDevice(deviceId);
+    if (device) {
+        outVirtualKeys = device->virtualKeys;
+    }
+}
+
+const std::shared_ptr<KeyCharacterMap> FakeEventHub::getKeyCharacterMap(int32_t) const {
+    return nullptr;
+}
+
+bool FakeEventHub::setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) {
+    return false;
+}
+
+std::vector<int32_t> FakeEventHub::getVibratorIds(int32_t deviceId) const {
+    return mVibrators;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryCapacity(int32_t, int32_t) const {
+    return BATTERY_CAPACITY;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryStatus(int32_t, int32_t) const {
+    return BATTERY_STATUS;
+}
+
+std::vector<int32_t> FakeEventHub::getRawBatteryIds(int32_t deviceId) const {
+    return {DEFAULT_BATTERY};
+}
+
+std::optional<RawBatteryInfo> FakeEventHub::getRawBatteryInfo(int32_t deviceId,
+                                                              int32_t batteryId) const {
+    if (batteryId != DEFAULT_BATTERY) return {};
+    static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
+                                                    .name = "default battery",
+                                                    .flags = InputBatteryClass::CAPACITY,
+                                                    .path = BATTERY_DEVPATH};
+    return BATTERY_INFO;
+}
+
+std::vector<int32_t> FakeEventHub::getRawLightIds(int32_t deviceId) const {
+    std::vector<int32_t> ids;
+    for (const auto& [rawId, info] : mRawLightInfos) {
+        ids.push_back(rawId);
+    }
+    return ids;
+}
+
+std::optional<RawLightInfo> FakeEventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const {
+    auto it = mRawLightInfos.find(lightId);
+    if (it == mRawLightInfos.end()) {
+        return std::nullopt;
+    }
+    return it->second;
+}
+
+void FakeEventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
+    mLightBrightness.emplace(lightId, brightness);
+}
+
+void FakeEventHub::setLightIntensities(int32_t deviceId, int32_t lightId,
+                                       std::unordered_map<LightColor, int32_t> intensities) {
+    mLightIntensities.emplace(lightId, intensities);
+};
+
+std::optional<int32_t> FakeEventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const {
+    auto lightIt = mLightBrightness.find(lightId);
+    if (lightIt == mLightBrightness.end()) {
+        return std::nullopt;
+    }
+    return lightIt->second;
+}
+
+std::optional<std::unordered_map<LightColor, int32_t>> FakeEventHub::getLightIntensities(
+        int32_t deviceId, int32_t lightId) const {
+    auto lightIt = mLightIntensities.find(lightId);
+    if (lightIt == mLightIntensities.end()) {
+        return std::nullopt;
+    }
+    return lightIt->second;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
new file mode 100644
index 0000000..fb3c859
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2022 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 <condition_variable>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <EventHub.h>
+#include <InputDevice.h>
+#include <ftl/flags.h>
+#include <input/PropertyMap.h>
+#include <input/VirtualKeyMap.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
+using android::hardware::input::InputDeviceCountryCode;
+
+namespace android {
+
+class FakeEventHub : public EventHubInterface {
+    struct KeyInfo {
+        int32_t keyCode;
+        uint32_t flags;
+    };
+
+    struct SensorInfo {
+        InputDeviceSensorType sensorType;
+        int32_t sensorDataIndex;
+    };
+
+    struct Device {
+        InputDeviceIdentifier identifier;
+        ftl::Flags<InputDeviceClass> classes;
+        PropertyMap configuration;
+        KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
+        KeyedVector<int, bool> relativeAxes;
+        KeyedVector<int32_t, int32_t> keyCodeStates;
+        KeyedVector<int32_t, int32_t> scanCodeStates;
+        KeyedVector<int32_t, int32_t> switchStates;
+        KeyedVector<int32_t, int32_t> absoluteAxisValue;
+        KeyedVector<int32_t, KeyInfo> keysByScanCode;
+        KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+        std::unordered_map<int32_t, int32_t> keyRemapping;
+        KeyedVector<int32_t, bool> leds;
+        // fake mapping which would normally come from keyCharacterMap
+        std::unordered_map<int32_t, int32_t> keyCodeMapping;
+        std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+        BitArray<MSC_MAX> mscBitmask;
+        std::vector<VirtualKeyDefinition> virtualKeys;
+        bool enabled;
+        InputDeviceCountryCode countryCode;
+
+        status_t enable() {
+            enabled = true;
+            return OK;
+        }
+
+        status_t disable() {
+            enabled = false;
+            return OK;
+        }
+
+        explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
+    };
+
+    std::mutex mLock;
+    std::condition_variable mEventsCondition;
+
+    KeyedVector<int32_t, Device*> mDevices;
+    std::vector<std::string> mExcludedDevices;
+    std::vector<RawEvent> mEvents GUARDED_BY(mLock);
+    std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+    std::vector<int32_t> mVibrators = {0, 1};
+    std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
+    // Simulates a device light brightness, from light id to light brightness.
+    std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
+    // Simulates a device light intensities, from light id to light intensities map.
+    std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
+            mLightIntensities;
+
+public:
+    static constexpr int32_t DEFAULT_BATTERY = 1;
+    static constexpr int32_t BATTERY_STATUS = 4;
+    static constexpr int32_t BATTERY_CAPACITY = 66;
+    static const std::string BATTERY_DEVPATH;
+
+    virtual ~FakeEventHub();
+    FakeEventHub() {}
+
+    void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes,
+                   int bus = 0);
+    void removeDevice(int32_t deviceId);
+
+    bool isDeviceEnabled(int32_t deviceId) const override;
+    status_t enableDevice(int32_t deviceId) override;
+    status_t disableDevice(int32_t deviceId) override;
+
+    void finishDeviceScan();
+
+    void addConfigurationProperty(int32_t deviceId, const char* key, const char* value);
+    void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration);
+
+    void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat,
+                         int fuzz, int resolution = 0);
+    void addRelativeAxis(int32_t deviceId, int32_t axis);
+    void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value);
+
+    void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode);
+
+    void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state);
+    void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state);
+    void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state);
+
+    void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+                uint32_t flags);
+    void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
+    void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
+    void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
+
+    void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+                       int32_t sensorDataIndex);
+
+    void setMscEvent(int32_t deviceId, int32_t mscEvent);
+
+    void addLed(int32_t deviceId, int32_t led, bool initialState);
+    void addRawLightInfo(int32_t rawId, RawLightInfo&& info);
+    void fakeLightBrightness(int32_t rawId, int32_t brightness);
+    void fakeLightIntensities(int32_t rawId,
+                              const std::unordered_map<LightColor, int32_t> intensities);
+    bool getLedState(int32_t deviceId, int32_t led);
+
+    std::vector<std::string>& getExcludedDevices();
+
+    void setVideoFrames(
+            std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames);
+
+    void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+                      int32_t value);
+    void assertQueueIsEmpty();
+
+private:
+    Device* getDevice(int32_t deviceId) const;
+
+    ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override;
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+    int32_t getDeviceControllerNumber(int32_t) const override;
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override;
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+    bool hasInputProperty(int32_t, int) const override;
+    bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                    int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override;
+    const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const;
+
+    status_t mapAxis(int32_t, int32_t, AxisInfo*) const override;
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) const override;
+    void setExcludedDevices(const std::vector<std::string>& devices) override;
+    std::vector<RawEvent> getEvents(int) override;
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override;
+    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
+
+    // Return true if the device has non-empty key layout.
+    bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+                               uint8_t* outFlags) const override;
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override;
+    bool hasLed(int32_t deviceId, int32_t led) const override;
+    void setLedState(int32_t deviceId, int32_t led, bool on) override;
+    void getVirtualKeyDefinitions(int32_t deviceId,
+                                  std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override;
+    bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override;
+
+    void vibrate(int32_t, const VibrationElement&) override {}
+    void cancelVibrate(int32_t) override {}
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override;
+
+    std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override;
+    std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override;
+    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override;
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t batteryId) const override;
+
+    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override;
+    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override;
+    void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override;
+    void setLightIntensities(int32_t deviceId, int32_t lightId,
+                             std::unordered_map<LightColor, int32_t> intensities) override;
+    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
+    std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+            int32_t deviceId, int32_t lightId) const override;
+
+    void dump(std::string&) const override {}
+    void monitor() const override {}
+    void requestReopenDevices() override {}
+    void wake() override {}
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
new file mode 100644
index 0000000..3af4298
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2022 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 "FakeInputReaderPolicy.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+
+#include "TestConstants.h"
+#include "ui/Rotation.h"
+
+namespace android {
+
+void FakeInputReaderPolicy::assertInputDevicesChanged() {
+    waitForInputDevices([](bool devicesChanged) {
+        if (!devicesChanged) {
+            FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+        }
+    });
+}
+
+void FakeInputReaderPolicy::assertInputDevicesNotChanged() {
+    waitForInputDevices([](bool devicesChanged) {
+        if (devicesChanged) {
+            FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+        }
+    });
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) {
+    std::scoped_lock lock(mLock);
+    ASSERT_TRUE(mStylusGestureNotified);
+    ASSERT_EQ(deviceId, *mStylusGestureNotified);
+    mStylusGestureNotified.reset();
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotNotified() {
+    std::scoped_lock lock(mLock);
+    ASSERT_FALSE(mStylusGestureNotified);
+}
+
+void FakeInputReaderPolicy::clearViewports() {
+    mViewports.clear();
+    mConfig.setDisplayViewports(mViewports);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByUniqueId(
+        const std::string& uniqueId) const {
+    return mConfig.getDisplayViewportByUniqueId(uniqueId);
+}
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByType(
+        ViewportType type) const {
+    return mConfig.getDisplayViewportByType(type);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByPort(
+        uint8_t displayPort) const {
+    return mConfig.getDisplayViewportByPort(displayPort);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(DisplayViewport viewport) {
+    mViewports.push_back(std::move(viewport));
+    mConfig.setDisplayViewports(mViewports);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+                                               ui::Rotation orientation, bool isActive,
+                                               const std::string& uniqueId,
+                                               std::optional<uint8_t> physicalPort,
+                                               ViewportType type) {
+    const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
+    DisplayViewport v;
+    v.displayId = displayId;
+    v.orientation = orientation;
+    v.logicalLeft = 0;
+    v.logicalTop = 0;
+    v.logicalRight = isRotated ? height : width;
+    v.logicalBottom = isRotated ? width : height;
+    v.physicalLeft = 0;
+    v.physicalTop = 0;
+    v.physicalRight = isRotated ? height : width;
+    v.physicalBottom = isRotated ? width : height;
+    v.deviceWidth = isRotated ? height : width;
+    v.deviceHeight = isRotated ? width : height;
+    v.isActive = isActive;
+    v.uniqueId = uniqueId;
+    v.physicalPort = physicalPort;
+    v.type = type;
+
+    addDisplayViewport(v);
+}
+
+bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) {
+    size_t count = mViewports.size();
+    for (size_t i = 0; i < count; i++) {
+        const DisplayViewport& currentViewport = mViewports[i];
+        if (currentViewport.displayId == viewport.displayId) {
+            mViewports[i] = viewport;
+            mConfig.setDisplayViewports(mViewports);
+            return true;
+        }
+    }
+    // no viewport found.
+    return false;
+}
+
+void FakeInputReaderPolicy::addExcludedDeviceName(const std::string& deviceName) {
+    mConfig.excludedDeviceNames.push_back(deviceName);
+}
+
+void FakeInputReaderPolicy::addInputPortAssociation(const std::string& inputPort,
+                                                    uint8_t displayPort) {
+    mConfig.portAssociations.insert({inputPort, displayPort});
+}
+
+void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
+                                                        const std::string& displayUniqueId) {
+    mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+}
+
+void FakeInputReaderPolicy::addDisabledDevice(int32_t deviceId) {
+    mConfig.disabledDevices.insert(deviceId);
+}
+
+void FakeInputReaderPolicy::removeDisabledDevice(int32_t deviceId) {
+    mConfig.disabledDevices.erase(deviceId);
+}
+
+void FakeInputReaderPolicy::setPointerController(
+        std::shared_ptr<FakePointerController> controller) {
+    mPointerController = std::move(controller);
+}
+
+const InputReaderConfiguration* FakeInputReaderPolicy::getReaderConfiguration() const {
+    return &mConfig;
+}
+
+const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const {
+    return mInputDevices;
+}
+
+TouchAffineTransformation FakeInputReaderPolicy::getTouchAffineTransformation(
+        const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
+    return transform;
+}
+
+void FakeInputReaderPolicy::setTouchAffineTransformation(const TouchAffineTransformation t) {
+    transform = t;
+}
+
+PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) {
+    mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+    return mConfig.pointerCaptureRequest;
+}
+
+void FakeInputReaderPolicy::setShowTouches(bool enabled) {
+    mConfig.showTouches = enabled;
+}
+
+void FakeInputReaderPolicy::setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+    mConfig.defaultPointerDisplayId = pointerDisplayId;
+}
+
+void FakeInputReaderPolicy::setPointerGestureEnabled(bool enabled) {
+    mConfig.pointerGesturesEnabled = enabled;
+}
+
+float FakeInputReaderPolicy::getPointerGestureMovementSpeedRatio() {
+    return mConfig.pointerGestureMovementSpeedRatio;
+}
+
+float FakeInputReaderPolicy::getPointerGestureZoomSpeedRatio() {
+    return mConfig.pointerGestureZoomSpeedRatio;
+}
+
+void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) {
+    mConfig.pointerVelocityControlParameters = params;
+    mConfig.wheelVelocityControlParameters = params;
+}
+
+void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
+    *outConfig = mConfig;
+}
+
+std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointerController(
+        int32_t /*deviceId*/) {
+    return mPointerController;
+}
+
+void FakeInputReaderPolicy::notifyInputDevicesChanged(
+        const std::vector<InputDeviceInfo>& inputDevices) {
+    std::scoped_lock<std::mutex> lock(mLock);
+    mInputDevices = inputDevices;
+    mInputDevicesChanged = true;
+    mDevicesChangedCondition.notify_all();
+}
+
+std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
+        const InputDeviceIdentifier&) {
+    return nullptr;
+}
+
+std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) {
+    return "";
+}
+
+void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    const bool devicesChanged =
+            mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                return mInputDevicesChanged;
+            });
+    ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
+    mInputDevicesChanged = false;
+}
+
+void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
+    std::scoped_lock<std::mutex> lock(mLock);
+    mStylusGestureNotified = deviceId;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
new file mode 100644
index 0000000..c16cda4
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 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 <condition_variable>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+
+#include "FakePointerController.h"
+#include "input/DisplayViewport.h"
+#include "input/InputDevice.h"
+
+namespace android {
+
+class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+protected:
+    virtual ~FakeInputReaderPolicy() {}
+
+public:
+    FakeInputReaderPolicy() {}
+
+    void assertInputDevicesChanged();
+    void assertInputDevicesNotChanged();
+    void assertStylusGestureNotified(int32_t deviceId);
+    void assertStylusGestureNotNotified();
+
+    virtual void clearViewports();
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
+    void addDisplayViewport(DisplayViewport viewport);
+    void addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+                            ui::Rotation orientation, bool isActive, const std::string& uniqueId,
+                            std::optional<uint8_t> physicalPort, ViewportType type);
+    bool updateViewport(const DisplayViewport& viewport);
+    void addExcludedDeviceName(const std::string& deviceName);
+    void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
+    void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+                                     const std::string& displayUniqueId);
+    void addDisabledDevice(int32_t deviceId);
+    void removeDisabledDevice(int32_t deviceId);
+    void setPointerController(std::shared_ptr<FakePointerController> controller);
+    const InputReaderConfiguration* getReaderConfiguration() const;
+    const std::vector<InputDeviceInfo>& getInputDevices() const;
+    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+                                                           ui::Rotation surfaceRotation);
+    void setTouchAffineTransformation(const TouchAffineTransformation t);
+    PointerCaptureRequest setPointerCapture(bool enabled);
+    void setShowTouches(bool enabled);
+    void setDefaultPointerDisplayId(int32_t pointerDisplayId);
+    void setPointerGestureEnabled(bool enabled);
+    float getPointerGestureMovementSpeedRatio();
+    float getPointerGestureZoomSpeedRatio();
+    void setVelocityControlParams(const VelocityControlParameters& params);
+
+private:
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(
+            int32_t /*deviceId*/) override;
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier&) override;
+    std::string getDeviceAlias(const InputDeviceIdentifier&) override;
+    void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
+    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
+
+    std::mutex mLock;
+    std::condition_variable mDevicesChangedCondition;
+
+    InputReaderConfiguration mConfig;
+    std::shared_ptr<FakePointerController> mPointerController;
+    std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+    bool mInputDevicesChanged GUARDED_BY(mLock){false};
+    std::vector<DisplayViewport> mViewports;
+    TouchAffineTransformation transform;
+    std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+
+    uint32_t mNextPointerCaptureSequenceNumber{0};
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
new file mode 100644
index 0000000..635366b
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 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 "FakePointerController.h"
+
+namespace android {
+
+void FakePointerController::setBounds(float minX, float minY, float maxX, float maxY) {
+    mHaveBounds = true;
+    mMinX = minX;
+    mMinY = minY;
+    mMaxX = maxX;
+    mMaxY = maxY;
+}
+
+const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
+    return mSpotsByDisplay;
+}
+
+void FakePointerController::setPosition(float x, float y) {
+    mX = x;
+    mY = y;
+}
+
+void FakePointerController::setButtonState(int32_t buttonState) {
+    mButtonState = buttonState;
+}
+
+int32_t FakePointerController::getButtonState() const {
+    return mButtonState;
+}
+
+void FakePointerController::getPosition(float* outX, float* outY) const {
+    *outX = mX;
+    *outY = mY;
+}
+
+int32_t FakePointerController::getDisplayId() const {
+    return mDisplayId;
+}
+
+void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
+    mDisplayId = viewport.displayId;
+}
+
+bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+                                      float* outMaxY) const {
+    *outMinX = mMinX;
+    *outMinY = mMinY;
+    *outMaxX = mMaxX;
+    *outMaxY = mMaxY;
+    return mHaveBounds;
+}
+
+void FakePointerController::move(float deltaX, float deltaY) {
+    mX += deltaX;
+    if (mX < mMinX) mX = mMinX;
+    if (mX > mMaxX) mX = mMaxX;
+    mY += deltaY;
+    if (mY < mMinY) mY = mMinY;
+    if (mY > mMaxY) mY = mMaxY;
+}
+
+void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+                                     int32_t displayId) {
+    std::vector<int32_t> newSpots;
+    // Add spots for fingers that are down.
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        newSpots.push_back(id);
+    }
+
+    mSpotsByDisplay[displayId] = newSpots;
+}
+
+void FakePointerController::clearSpots() {
+    mSpotsByDisplay.clear();
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
new file mode 100644
index 0000000..f00870f
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 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 <PointerControllerInterface.h>
+#include <gui/constants.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <utils/BitSet.h>
+
+namespace android {
+
+class FakePointerController : public PointerControllerInterface {
+public:
+    virtual ~FakePointerController() {}
+
+    void setBounds(float minX, float minY, float maxX, float maxY);
+    const std::map<int32_t, std::vector<int32_t>>& getSpots();
+
+    void setPosition(float x, float y) override;
+    void setButtonState(int32_t buttonState) override;
+    int32_t getButtonState() const override;
+    void getPosition(float* outX, float* outY) const override;
+    int32_t getDisplayId() const override;
+    void setDisplayViewport(const DisplayViewport& viewport) override;
+
+private:
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
+    void move(float deltaX, float deltaY) override;
+    void fade(Transition) override {}
+    void unfade(Transition) override {}
+    void setPresentation(Presentation) override {}
+    void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+                  int32_t displayId) override;
+    void clearSpots() override;
+
+    bool mHaveBounds{false};
+    float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
+    float mX{0}, mY{0};
+    int32_t mButtonState{0};
+    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+
+    std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 689fe5c..41c174a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -110,6 +110,13 @@
         *result_listener << "expected action " << MotionEvent::actionToString(action)
                          << ", but got " << MotionEvent::actionToString(arg.getAction());
     }
+    if (action == AMOTION_EVENT_ACTION_DOWN) {
+        if (!matches) {
+            *result_listener << "; ";
+        }
+        *result_listener << "downTime should match eventTime for ACTION_DOWN events";
+        matches &= arg.getDownTime() == arg.getEventTime();
+    }
     if (action == AMOTION_EVENT_ACTION_CANCEL) {
         if (!matches) {
             *result_listener << "; ";
@@ -120,6 +127,10 @@
     return matches;
 }
 
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+    return arg.getDownTime() == downTime;
+}
+
 MATCHER_P(WithSource, source, "InputEvent with specified source") {
     *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
                      << inputEventSourceToString(arg.getSource());
@@ -1210,6 +1221,7 @@
     void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
                                               int32_t expectedFlags = 0) {
         InputEvent* event = consume();
+        ASSERT_NE(nullptr, event);
         ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
         const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
         EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
@@ -1231,7 +1243,7 @@
 
     void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
         MotionEvent* motionEvent = consumeMotion();
-        ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
+        ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
         ASSERT_THAT(*motionEvent, matcher);
     }
 
@@ -1922,6 +1934,64 @@
     wallpaperWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    window->setDupTouchToWallpaper(true);
+
+    sp<FakeWindowHandle> wallpaperWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->setIsWallpaper(true);
+    constexpr int expectedWallpaperFlags =
+            AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+    wallpaperWindow->setPreventSplitting(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    window->consumeMotionPointerDown(1);
+    wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    const MotionEvent secondFingerUpEvent =
+            MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionPointerUp(1);
+    wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
 /**
  * On the display, have a single window, and also an area where there's no window.
  * First pointer touches the "no window" area of the screen. Second pointer touches the window.
@@ -1998,6 +2068,7 @@
 
     mDispatcher->waitForIdle();
     InputEvent* inputEvent1 = window1->consume();
+    ASSERT_NE(inputEvent1, nullptr);
     window2->assertNoEvents();
     MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
     nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
@@ -2007,6 +2078,7 @@
     mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
     mDispatcher->waitForIdle();
     InputEvent* inputEvent2 = window2->consume();
+    ASSERT_NE(inputEvent2, nullptr);
     MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
     nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
     ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
@@ -2016,17 +2088,13 @@
     mDispatcher->notifyMotion(
             &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
     mDispatcher->waitForIdle();
-    InputEvent* inputEvent3 = window2->consume();
-    MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
-    ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+    window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
 
     // Now add new touch down on the second window
     mDispatcher->notifyMotion(
             &(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
     mDispatcher->waitForIdle();
-    InputEvent* inputEvent4 = window2->consume();
-    MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
-    ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+    window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
 
     // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
     window1->consumeMotionMove();
@@ -2036,16 +2104,12 @@
     mDispatcher->notifyMotion(
             &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
     mDispatcher->waitForIdle();
-    InputEvent* inputEvent5 = window1->consume();
-    MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
-    ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+    window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
 
     mDispatcher->notifyMotion(&(
             args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
     mDispatcher->waitForIdle();
-    InputEvent* inputEvent6 = window1->consume();
-    MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
-    ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+    window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
 }
 
 TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -2070,10 +2134,8 @@
                                                          .x(900)
                                                          .y(400))
                                         .build()));
-    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
-    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
-                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+    windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
 
     // Move cursor into left window
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2084,12 +2146,9 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
-                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
-                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+    windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+    windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
 
     // Inject a series of mouse events for a mouse click
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2112,8 +2171,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(mDispatcher,
@@ -2125,8 +2183,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(mDispatcher,
@@ -2147,12 +2204,47 @@
                                                          .x(900)
                                                          .y(400))
                                         .build()));
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
-                             ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
-    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
-    windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
-                              ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+    windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+    windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+    // No more events
+    windowLeft->assertNoEvents();
+    windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    sp<FakeWindowHandle> spyWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+    spyWindow->setFrame(Rect(0, 0, 600, 800));
+    spyWindow->setTrustedOverlay(true);
+    spyWindow->setSpy(true);
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 600, 800));
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+    // Send mouse cursor to the window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(100)
+                                                         .y(100))
+                                        .build()));
+
+    window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                     WithSource(AINPUT_SOURCE_MOUSE)));
+    spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                        WithSource(AINPUT_SOURCE_MOUSE)));
+
+    window->assertNoEvents();
+    spyWindow->assertNoEvents();
 }
 
 // This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
@@ -2175,8 +2267,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
 
     // Inject a series of mouse events for a mouse click
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2199,8 +2290,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(mDispatcher,
@@ -2212,8 +2302,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionEvent(mDispatcher,
@@ -2233,8 +2322,7 @@
                                                          .x(300)
                                                          .y(400))
                                         .build()));
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
-                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
 }
 
 /**
@@ -2279,6 +2367,59 @@
                                              WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
 }
 
+TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> windowDefaultDisplay =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay",
+                                       ADISPLAY_ID_DEFAULT);
+    windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800));
+    sp<FakeWindowHandle> windowSecondDisplay =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay",
+                                       SECOND_DISPLAY_ID);
+    windowSecondDisplay->setFrame(Rect(0, 0, 600, 800));
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+                                  {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+
+    // Set cursor position in window in default display and check that hover enter and move
+    // events are generated.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .displayId(ADISPLAY_ID_DEFAULT)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(300)
+                                                         .y(600))
+                                        .build()));
+    windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+    windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+    // Remove all windows in secondary display and check that no event happens on window in
+    // primary display.
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
+    windowDefaultDisplay->assertNoEvents();
+
+    // Move cursor position in window in default display and check that only hover move
+    // event is generated and not hover enter event.
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+                                  {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                   AINPUT_SOURCE_MOUSE)
+                                        .displayId(ADISPLAY_ID_DEFAULT)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+                                                         .x(400)
+                                                         .y(700))
+                                        .build()));
+    windowDefaultDisplay->consumeMotionEvent(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE)));
+    windowDefaultDisplay->assertNoEvents();
+}
+
 TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
@@ -2472,6 +2613,43 @@
     window->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+                                                             "Fake Window", ADISPLAY_ID_DEFAULT);
+    // Ensure window is non-split and have some transform.
+    window->setPreventSplitting(true);
+    window->setWindowOffset(20, 40);
+    mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(-30)
+                                     .y(-50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    const MotionEvent* event = window->consumeMotion();
+    EXPECT_EQ(POINTER_1_DOWN, event->getAction());
+    EXPECT_EQ(70, event->getX(0));  // 50 + 20
+    EXPECT_EQ(90, event->getY(0));  // 50 + 40
+    EXPECT_EQ(-10, event->getX(1)); // -30 + 20
+    EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+}
+
 /**
  * Ensure the correct coordinate spaces are used by InputDispatcher.
  *
@@ -4521,6 +4699,7 @@
 
         const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
         assertMotionAction(expectedAction, motionEvent.getAction());
+        ASSERT_EQ(points.size(), motionEvent.getPointerCount());
 
         for (size_t i = 0; i < points.size(); i++) {
             float expectedX = points[i].x;
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
new file mode 100644
index 0000000..3cd7c1b
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2022 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 "InputMapperTest.h"
+
+#include <InputReaderBase.h>
+#include <gtest/gtest.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
+const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+        ftl::Flags<InputDeviceClass>(0); // not needed for current tests
+
+void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) {
+    mFakeEventHub = std::make_unique<FakeEventHub>();
+    mFakePolicy = sp<FakeInputReaderPolicy>::make();
+    mFakeListener = std::make_unique<TestInputListener>();
+    mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener);
+    mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
+    // Consume the device reset notification generated when adding a new device.
+    mFakeListener->assertNotifyDeviceResetWasCalled();
+}
+
+void InputMapperTest::SetUp() {
+    SetUp(DEVICE_CLASSES);
+}
+
+void InputMapperTest::TearDown() {
+    mFakeListener.reset();
+    mFakePolicy.clear();
+}
+
+void InputMapperTest::addConfigurationProperty(const char* key, const char* value) {
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
+}
+
+std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
+    if (!changes ||
+        (changes &
+         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+          InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+        mReader->requestRefreshConfiguration(changes);
+        mReader->loopOnce();
+    }
+    std::list<NotifyArgs> out =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+    // Loop the reader to flush the input listener queue.
+    for (const NotifyArgs& args : out) {
+        mFakeListener->notify(args);
+    }
+    mReader->loopOnce();
+    return out;
+}
+
+std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name,
+                                                        const std::string& location,
+                                                        int32_t eventHubId,
+                                                        ftl::Flags<InputDeviceClass> classes,
+                                                        int bus) {
+    InputDeviceIdentifier identifier;
+    identifier.name = name;
+    identifier.location = location;
+    identifier.bus = bus;
+    std::shared_ptr<InputDevice> device =
+            std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+                                          identifier);
+    mReader->pushNextDevice(device);
+    mFakeEventHub->addDevice(eventHubId, name, classes, bus);
+    mReader->loopOnce();
+    return device;
+}
+
+void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+                                                   ui::Rotation orientation,
+                                                   const std::string& uniqueId,
+                                                   std::optional<uint8_t> physicalPort,
+                                                   ViewportType viewportType) {
+    mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
+                                    uniqueId, physicalPort, viewportType);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void InputMapperTest::clearViewports() {
+    mFakePolicy->clearViewports();
+}
+
+std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime,
+                                               int32_t type, int32_t code, int32_t value) {
+    RawEvent event;
+    event.when = when;
+    event.readTime = readTime;
+    event.deviceId = mapper.getDeviceContext().getEventHubId();
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    std::list<NotifyArgs> processArgList = mapper.process(&event);
+    for (const NotifyArgs& args : processArgList) {
+        mFakeListener->notify(args);
+    }
+    // Loop the reader to flush the input listener queue.
+    mReader->loopOnce();
+    return processArgList;
+}
+
+void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) {
+    const auto resetArgs = mapper.reset(when);
+    for (const auto args : resetArgs) {
+        mFakeListener->notify(args);
+    }
+    // Loop the reader to flush the input listener queue.
+    mReader->loopOnce();
+}
+
+std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) {
+    std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+    for (const NotifyArgs& args : generatedArgs) {
+        mFakeListener->notify(args);
+    }
+    // Loop the reader to flush the input listener queue.
+    mReader->loopOnce();
+    return generatedArgs;
+}
+
+void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+                                        float min, float max, float flat, float fuzz) {
+    const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
+    ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
+    ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
+    ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+    ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+    ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+    ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+    ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
+}
+
+void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
+                                          float pressure, float size, float touchMajor,
+                                          float touchMinor, float toolMajor, float toolMinor,
+                                          float orientation, float distance,
+                                          float scaledAxisEpsilon) {
+    ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
+    ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
+    ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
+    ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
+    ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon);
+    ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon);
+    ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon);
+    ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon);
+    ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+    ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+}
+
+void InputMapperTest::assertPosition(const FakePointerController& controller, float x, float y) {
+    float actualX, actualY;
+    controller.getPosition(&actualX, &actualY);
+    ASSERT_NEAR(x, actualX, 1);
+    ASSERT_NEAR(y, actualY, 1);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
new file mode 100644
index 0000000..b3401c3
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 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 <list>
+#include <memory>
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class InputMapperTest : public testing::Test {
+protected:
+    static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
+    static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+    static constexpr int32_t DEVICE_GENERATION = 2;
+    static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0;
+    static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
+    static constexpr int32_t EVENTHUB_ID = 1;
+
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
+    sp<FakeInputReaderPolicy> mFakePolicy;
+    std::unique_ptr<TestInputListener> mFakeListener;
+    std::unique_ptr<InstrumentedInputReader> mReader;
+    std::shared_ptr<InputDevice> mDevice;
+
+    virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0);
+    void SetUp() override;
+    void TearDown() override;
+
+    void addConfigurationProperty(const char* key, const char* value);
+    std::list<NotifyArgs> configureDevice(uint32_t changes);
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location, int32_t eventHubId,
+                                           ftl::Flags<InputDeviceClass> classes, int bus = 0);
+    template <class T, typename... Args>
+    T& addMapperAndConfigure(Args... args) {
+        T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+        configureDevice(0);
+        std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+        resetArgList += mapper.reset(ARBITRARY_TIME);
+        // Loop the reader to flush the input listener queue.
+        for (const NotifyArgs& loopArgs : resetArgList) {
+            mFakeListener->notify(loopArgs);
+        }
+        mReader->loopOnce();
+        return mapper;
+    }
+
+    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+                                      ui::Rotation orientation, const std::string& uniqueId,
+                                      std::optional<uint8_t> physicalPort,
+                                      ViewportType viewportType);
+    void clearViewports();
+    std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+                                  int32_t code, int32_t value);
+    void resetMapper(InputMapper& mapper, nsecs_t when);
+
+    std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
+
+    static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+                                  float min, float max, float flat, float fuzz);
+    static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
+                                    float size, float touchMajor, float touchMinor, float toolMajor,
+                                    float toolMinor, float orientation, float distance,
+                                    float scaledAxisEpsilon = 1.f);
+    static void assertPosition(const FakePointerController& controller, float x, float y);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 6b6d0bb..4cc48f6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -36,10 +36,18 @@
 #include <UinputDevice.h>
 #include <VibratorInputMapper.h>
 #include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
 #include <gtest/gtest.h>
 #include <gui/constants.h>
+#include <ui/Rotation.h>
 
 #include <thread>
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "FakePointerController.h"
+#include "InputMapperTest.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
 #include "android/hardware/input/InputDeviceCountryCode.h"
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
@@ -52,13 +60,6 @@
 using testing::AllOf;
 using std::chrono_literals::operator""ms;
 
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
-
-// An arbitrary time value.
-static constexpr nsecs_t ARBITRARY_TIME = 1234;
-static constexpr nsecs_t READ_TIME = 4321;
-
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
 static const std::string DISPLAY_UNIQUE_ID = "local:1";
@@ -79,10 +80,6 @@
 static constexpr int32_t FIRST_TRACKING_ID = 0;
 static constexpr int32_t SECOND_TRACKING_ID = 1;
 static constexpr int32_t THIRD_TRACKING_ID = 2;
-static constexpr int32_t DEFAULT_BATTERY = 1;
-static constexpr int32_t BATTERY_STATUS = 4;
-static constexpr int32_t BATTERY_CAPACITY = 66;
-static const std::string BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
 static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
 static constexpr int32_t LIGHT_COLOR = 0x7F448866;
 static constexpr int32_t LIGHT_PLAYER_ID = 2;
@@ -96,9 +93,6 @@
 static constexpr int32_t ACTION_POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
-
 // Minimum timestamp separation between subsequent input events from a Bluetooth device.
 static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
 // Maximum smoothing time delta so that we don't generate events too far into the future.
@@ -118,12 +112,12 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
-static int32_t getInverseRotation(int32_t orientation) {
+static ui::Rotation getInverseRotation(ui::Rotation orientation) {
     switch (orientation) {
-        case DISPLAY_ORIENTATION_90:
-            return DISPLAY_ORIENTATION_270;
-        case DISPLAY_ORIENTATION_270:
-            return DISPLAY_ORIENTATION_90;
+        case ui::ROTATION_90:
+            return ui::ROTATION_270;
+        case ui::ROTATION_270:
+            return ui::ROTATION_90;
         default:
             return orientation;
     }
@@ -157,960 +151,6 @@
     }
 }
 
-// --- FakePointerController ---
-
-class FakePointerController : public PointerControllerInterface {
-    bool mHaveBounds;
-    float mMinX, mMinY, mMaxX, mMaxY;
-    float mX, mY;
-    int32_t mButtonState;
-    int32_t mDisplayId;
-
-public:
-    FakePointerController() :
-        mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
-        mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
-    }
-
-    virtual ~FakePointerController() {}
-
-    void setBounds(float minX, float minY, float maxX, float maxY) {
-        mHaveBounds = true;
-        mMinX = minX;
-        mMinY = minY;
-        mMaxX = maxX;
-        mMaxY = maxY;
-    }
-
-    void setPosition(float x, float y) override {
-        mX = x;
-        mY = y;
-    }
-
-    void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
-
-    int32_t getButtonState() const override { return mButtonState; }
-
-    void getPosition(float* outX, float* outY) const override {
-        *outX = mX;
-        *outY = mY;
-    }
-
-    int32_t getDisplayId() const override { return mDisplayId; }
-
-    void setDisplayViewport(const DisplayViewport& viewport) override {
-        mDisplayId = viewport.displayId;
-    }
-
-    const std::map<int32_t, std::vector<int32_t>>& getSpots() {
-        return mSpotsByDisplay;
-    }
-
-private:
-    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
-        *outMinX = mMinX;
-        *outMinY = mMinY;
-        *outMaxX = mMaxX;
-        *outMaxY = mMaxY;
-        return mHaveBounds;
-    }
-
-    void move(float deltaX, float deltaY) override {
-        mX += deltaX;
-        if (mX < mMinX) mX = mMinX;
-        if (mX > mMaxX) mX = mMaxX;
-        mY += deltaY;
-        if (mY < mMinY) mY = mMinY;
-        if (mY > mMaxY) mY = mMaxY;
-    }
-
-    void fade(Transition) override {}
-
-    void unfade(Transition) override {}
-
-    void setPresentation(Presentation) override {}
-
-    void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
-                  int32_t displayId) override {
-        std::vector<int32_t> newSpots;
-        // Add spots for fingers that are down.
-        for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-            uint32_t id = idBits.clearFirstMarkedBit();
-            newSpots.push_back(id);
-        }
-
-        mSpotsByDisplay[displayId] = newSpots;
-    }
-
-    void clearSpots() override { mSpotsByDisplay.clear(); }
-
-    std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
-};
-
-
-// --- FakeInputReaderPolicy ---
-
-class FakeInputReaderPolicy : public InputReaderPolicyInterface {
-    std::mutex mLock;
-    std::condition_variable mDevicesChangedCondition;
-
-    InputReaderConfiguration mConfig;
-    std::shared_ptr<FakePointerController> mPointerController;
-    std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
-    bool mInputDevicesChanged GUARDED_BY(mLock){false};
-    std::vector<DisplayViewport> mViewports;
-    TouchAffineTransformation transform;
-    std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
-
-protected:
-    virtual ~FakeInputReaderPolicy() {}
-
-public:
-    FakeInputReaderPolicy() {
-    }
-
-    void assertInputDevicesChanged() {
-        waitForInputDevices([](bool devicesChanged) {
-            if (!devicesChanged) {
-                FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
-            }
-        });
-    }
-
-    void assertInputDevicesNotChanged() {
-        waitForInputDevices([](bool devicesChanged) {
-            if (devicesChanged) {
-                FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
-            }
-        });
-    }
-
-    void assertStylusGestureNotified(int32_t deviceId) {
-        std::scoped_lock lock(mLock);
-        ASSERT_TRUE(mStylusGestureNotified);
-        ASSERT_EQ(deviceId, *mStylusGestureNotified);
-        mStylusGestureNotified.reset();
-    }
-
-    void assertStylusGestureNotNotified() {
-        std::scoped_lock lock(mLock);
-        ASSERT_FALSE(mStylusGestureNotified);
-    }
-
-    virtual void clearViewports() {
-        mViewports.clear();
-        mConfig.setDisplayViewports(mViewports);
-    }
-
-    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
-        return mConfig.getDisplayViewportByUniqueId(uniqueId);
-    }
-    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
-        return mConfig.getDisplayViewportByType(type);
-    }
-
-    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
-        return mConfig.getDisplayViewportByPort(displayPort);
-    }
-
-    void addDisplayViewport(DisplayViewport viewport) {
-        mViewports.push_back(std::move(viewport));
-        mConfig.setDisplayViewports(mViewports);
-    }
-
-    void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-                            bool isActive, const std::string& uniqueId,
-                            std::optional<uint8_t> physicalPort, ViewportType type) {
-        const bool isRotated =
-                (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
-        DisplayViewport v;
-        v.displayId = displayId;
-        v.orientation = orientation;
-        v.logicalLeft = 0;
-        v.logicalTop = 0;
-        v.logicalRight = isRotated ? height : width;
-        v.logicalBottom = isRotated ? width : height;
-        v.physicalLeft = 0;
-        v.physicalTop = 0;
-        v.physicalRight = isRotated ? height : width;
-        v.physicalBottom = isRotated ? width : height;
-        v.deviceWidth = isRotated ? height : width;
-        v.deviceHeight = isRotated ? width : height;
-        v.isActive = isActive;
-        v.uniqueId = uniqueId;
-        v.physicalPort = physicalPort;
-        v.type = type;
-
-        addDisplayViewport(v);
-    }
-
-    bool updateViewport(const DisplayViewport& viewport) {
-        size_t count = mViewports.size();
-        for (size_t i = 0; i < count; i++) {
-            const DisplayViewport& currentViewport = mViewports[i];
-            if (currentViewport.displayId == viewport.displayId) {
-                mViewports[i] = viewport;
-                mConfig.setDisplayViewports(mViewports);
-                return true;
-            }
-        }
-        // no viewport found.
-        return false;
-    }
-
-    void addExcludedDeviceName(const std::string& deviceName) {
-        mConfig.excludedDeviceNames.push_back(deviceName);
-    }
-
-    void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
-        mConfig.portAssociations.insert({inputPort, displayPort});
-    }
-
-    void addInputUniqueIdAssociation(const std::string& inputUniqueId,
-                                     const std::string& displayUniqueId) {
-        mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
-    }
-
-    void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
-
-    void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
-
-    void setPointerController(std::shared_ptr<FakePointerController> controller) {
-        mPointerController = std::move(controller);
-    }
-
-    const InputReaderConfiguration* getReaderConfiguration() const {
-        return &mConfig;
-    }
-
-    const std::vector<InputDeviceInfo>& getInputDevices() const {
-        return mInputDevices;
-    }
-
-    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
-            int32_t surfaceRotation) {
-        return transform;
-    }
-
-    void setTouchAffineTransformation(const TouchAffineTransformation t) {
-        transform = t;
-    }
-
-    PointerCaptureRequest setPointerCapture(bool enabled) {
-        mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
-        return mConfig.pointerCaptureRequest;
-    }
-
-    void setShowTouches(bool enabled) {
-        mConfig.showTouches = enabled;
-    }
-
-    void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
-        mConfig.defaultPointerDisplayId = pointerDisplayId;
-    }
-
-    void setPointerGestureEnabled(bool enabled) { mConfig.pointerGesturesEnabled = enabled; }
-
-    float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
-
-    float getPointerGestureZoomSpeedRatio() { return mConfig.pointerGestureZoomSpeedRatio; }
-
-    void setVelocityControlParams(const VelocityControlParameters& params) {
-        mConfig.pointerVelocityControlParameters = params;
-        mConfig.wheelVelocityControlParameters = params;
-    }
-
-private:
-    uint32_t mNextPointerCaptureSequenceNumber = 0;
-
-    void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
-        *outConfig = mConfig;
-    }
-
-    std::shared_ptr<PointerControllerInterface> obtainPointerController(
-            int32_t /*deviceId*/) override {
-        return mPointerController;
-    }
-
-    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
-        std::scoped_lock<std::mutex> lock(mLock);
-        mInputDevices = inputDevices;
-        mInputDevicesChanged = true;
-        mDevicesChangedCondition.notify_all();
-    }
-
-    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
-            const InputDeviceIdentifier&) override {
-        return nullptr;
-    }
-
-    std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
-
-    void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
-        std::unique_lock<std::mutex> lock(mLock);
-        base::ScopedLockAssertion assumeLocked(mLock);
-
-        const bool devicesChanged =
-                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
-                    return mInputDevicesChanged;
-                });
-        ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
-        mInputDevicesChanged = false;
-    }
-
-    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
-        std::scoped_lock<std::mutex> lock(mLock);
-        mStylusGestureNotified = deviceId;
-    }
-};
-
-// --- FakeEventHub ---
-
-class FakeEventHub : public EventHubInterface {
-    struct KeyInfo {
-        int32_t keyCode;
-        uint32_t flags;
-    };
-
-    struct SensorInfo {
-        InputDeviceSensorType sensorType;
-        int32_t sensorDataIndex;
-    };
-
-    struct Device {
-        InputDeviceIdentifier identifier;
-        ftl::Flags<InputDeviceClass> classes;
-        PropertyMap configuration;
-        KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
-        KeyedVector<int, bool> relativeAxes;
-        KeyedVector<int32_t, int32_t> keyCodeStates;
-        KeyedVector<int32_t, int32_t> scanCodeStates;
-        KeyedVector<int32_t, int32_t> switchStates;
-        KeyedVector<int32_t, int32_t> absoluteAxisValue;
-        KeyedVector<int32_t, KeyInfo> keysByScanCode;
-        KeyedVector<int32_t, KeyInfo> keysByUsageCode;
-        KeyedVector<int32_t, bool> leds;
-        // fake mapping which would normally come from keyCharacterMap
-        std::unordered_map<int32_t, int32_t> keyCodeMapping;
-        std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
-        BitArray<MSC_MAX> mscBitmask;
-        std::vector<VirtualKeyDefinition> virtualKeys;
-        bool enabled;
-        InputDeviceCountryCode countryCode;
-
-        status_t enable() {
-            enabled = true;
-            return OK;
-        }
-
-        status_t disable() {
-            enabled = false;
-            return OK;
-        }
-
-        explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
-    };
-
-    std::mutex mLock;
-    std::condition_variable mEventsCondition;
-
-    KeyedVector<int32_t, Device*> mDevices;
-    std::vector<std::string> mExcludedDevices;
-    std::vector<RawEvent> mEvents GUARDED_BY(mLock);
-    std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
-    std::vector<int32_t> mVibrators = {0, 1};
-    std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
-    // Simulates a device light brightness, from light id to light brightness.
-    std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
-    // Simulates a device light intensities, from light id to light intensities map.
-    std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
-            mLightIntensities;
-
-public:
-    virtual ~FakeEventHub() {
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            delete mDevices.valueAt(i);
-        }
-    }
-
-    FakeEventHub() { }
-
-    void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes,
-                   int bus = 0) {
-        Device* device = new Device(classes);
-        device->identifier.name = name;
-        device->identifier.bus = bus;
-        mDevices.add(deviceId, device);
-
-        enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
-    }
-
-    void removeDevice(int32_t deviceId) {
-        delete mDevices.valueFor(deviceId);
-        mDevices.removeItem(deviceId);
-
-        enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
-    }
-
-    bool isDeviceEnabled(int32_t deviceId) const override {
-        Device* device = getDevice(deviceId);
-        if (device == nullptr) {
-            ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
-            return false;
-        }
-        return device->enabled;
-    }
-
-    status_t enableDevice(int32_t deviceId) override {
-        status_t result;
-        Device* device = getDevice(deviceId);
-        if (device == nullptr) {
-            ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
-            return BAD_VALUE;
-        }
-        if (device->enabled) {
-            ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
-            return OK;
-        }
-        result = device->enable();
-        return result;
-    }
-
-    status_t disableDevice(int32_t deviceId) override {
-        Device* device = getDevice(deviceId);
-        if (device == nullptr) {
-            ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
-            return BAD_VALUE;
-        }
-        if (!device->enabled) {
-            ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
-            return OK;
-        }
-        return device->disable();
-    }
-
-    void finishDeviceScan() {
-        enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
-    }
-
-    void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
-        Device* device = getDevice(deviceId);
-        device->configuration.addProperty(key, value);
-    }
-
-    void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
-        Device* device = getDevice(deviceId);
-        device->configuration.addAll(configuration);
-    }
-
-    void addAbsoluteAxis(int32_t deviceId, int axis,
-            int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) {
-        Device* device = getDevice(deviceId);
-
-        RawAbsoluteAxisInfo info;
-        info.valid = true;
-        info.minValue = minValue;
-        info.maxValue = maxValue;
-        info.flat = flat;
-        info.fuzz = fuzz;
-        info.resolution = resolution;
-        device->absoluteAxes.add(axis, info);
-    }
-
-    void addRelativeAxis(int32_t deviceId, int32_t axis) {
-        Device* device = getDevice(deviceId);
-        device->relativeAxes.add(axis, true);
-    }
-
-    void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
-        Device* device = getDevice(deviceId);
-        device->keyCodeStates.replaceValueFor(keyCode, state);
-    }
-
-    void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
-        Device* device = getDevice(deviceId);
-        device->countryCode = countryCode;
-    }
-
-    void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
-        Device* device = getDevice(deviceId);
-        device->scanCodeStates.replaceValueFor(scanCode, state);
-    }
-
-    void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
-        Device* device = getDevice(deviceId);
-        device->switchStates.replaceValueFor(switchCode, state);
-    }
-
-    void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
-        Device* device = getDevice(deviceId);
-        device->absoluteAxisValue.replaceValueFor(axis, value);
-    }
-
-    void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
-            int32_t keyCode, uint32_t flags) {
-        Device* device = getDevice(deviceId);
-        KeyInfo info;
-        info.keyCode = keyCode;
-        info.flags = flags;
-        if (scanCode) {
-            device->keysByScanCode.add(scanCode, info);
-        }
-        if (usageCode) {
-            device->keysByUsageCode.add(usageCode, info);
-        }
-    }
-
-    void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
-        Device* device = getDevice(deviceId);
-        device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
-    }
-
-    void addLed(int32_t deviceId, int32_t led, bool initialState) {
-        Device* device = getDevice(deviceId);
-        device->leds.add(led, initialState);
-    }
-
-    void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
-                       int32_t sensorDataIndex) {
-        Device* device = getDevice(deviceId);
-        SensorInfo info;
-        info.sensorType = sensorType;
-        info.sensorDataIndex = sensorDataIndex;
-        device->sensorsByAbsCode.emplace(absCode, info);
-    }
-
-    void setMscEvent(int32_t deviceId, int32_t mscEvent) {
-        Device* device = getDevice(deviceId);
-        typename BitArray<MSC_MAX>::Buffer buffer;
-        buffer[mscEvent / 32] = 1 << mscEvent % 32;
-        device->mscBitmask.loadFromBuffer(buffer);
-    }
-
-    void addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
-        mRawLightInfos.emplace(rawId, std::move(info));
-    }
-
-    void fakeLightBrightness(int32_t rawId, int32_t brightness) {
-        mLightBrightness.emplace(rawId, brightness);
-    }
-
-    void fakeLightIntensities(int32_t rawId,
-                              const std::unordered_map<LightColor, int32_t> intensities) {
-        mLightIntensities.emplace(rawId, std::move(intensities));
-    }
-
-    bool getLedState(int32_t deviceId, int32_t led) {
-        Device* device = getDevice(deviceId);
-        return device->leds.valueFor(led);
-    }
-
-    std::vector<std::string>& getExcludedDevices() {
-        return mExcludedDevices;
-    }
-
-    void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
-        Device* device = getDevice(deviceId);
-        device->virtualKeys.push_back(definition);
-    }
-
-    void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
-                      int32_t value) {
-        std::scoped_lock<std::mutex> lock(mLock);
-        RawEvent event;
-        event.when = when;
-        event.readTime = readTime;
-        event.deviceId = deviceId;
-        event.type = type;
-        event.code = code;
-        event.value = value;
-        mEvents.push_back(event);
-
-        if (type == EV_ABS) {
-            setAbsoluteAxisValue(deviceId, code, value);
-        }
-    }
-
-    void setVideoFrames(std::unordered_map<int32_t /*deviceId*/,
-            std::vector<TouchVideoFrame>> videoFrames) {
-        mVideoFrames = std::move(videoFrames);
-    }
-
-    void assertQueueIsEmpty() {
-        std::unique_lock<std::mutex> lock(mLock);
-        base::ScopedLockAssertion assumeLocked(mLock);
-        const bool queueIsEmpty =
-                mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
-                                          [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
-        if (!queueIsEmpty) {
-            FAIL() << "Timed out waiting for EventHub queue to be emptied.";
-        }
-    }
-
-private:
-    Device* getDevice(int32_t deviceId) const {
-        ssize_t index = mDevices.indexOfKey(deviceId);
-        return index >= 0 ? mDevices.valueAt(index) : nullptr;
-    }
-
-    ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
-        Device* device = getDevice(deviceId);
-        return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
-    }
-
-    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
-        Device* device = getDevice(deviceId);
-        return device ? device->identifier : InputDeviceIdentifier();
-    }
-
-    int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
-
-    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            *outConfiguration = device->configuration;
-        }
-    }
-
-    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->absoluteAxes.indexOfKey(axis);
-            if (index >= 0) {
-                *outAxisInfo = device->absoluteAxes.valueAt(index);
-                return OK;
-            }
-        }
-        outAxisInfo->clear();
-        return -1;
-    }
-
-    bool hasRelativeAxis(int32_t deviceId, int axis) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            return device->relativeAxes.indexOfKey(axis) >= 0;
-        }
-        return false;
-    }
-
-    bool hasInputProperty(int32_t, int) const override { return false; }
-
-    bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
-        }
-        return false;
-    }
-
-    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
-                    int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            const KeyInfo* key = getKey(device, scanCode, usageCode);
-            if (key) {
-                if (outKeycode) {
-                    *outKeycode = key->keyCode;
-                }
-                if (outFlags) {
-                    *outFlags = key->flags;
-                }
-                if (outMetaState) {
-                    *outMetaState = metaState;
-                }
-                return OK;
-            }
-        }
-        return NAME_NOT_FOUND;
-    }
-
-    const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const {
-        if (usageCode) {
-            ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
-            if (index >= 0) {
-                return &device->keysByUsageCode.valueAt(index);
-            }
-        }
-        if (scanCode) {
-            ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
-            if (index >= 0) {
-                return &device->keysByScanCode.valueAt(index);
-            }
-        }
-        return nullptr;
-    }
-
-    status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
-
-    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
-            int32_t deviceId, int32_t absCode) const override {
-        Device* device = getDevice(deviceId);
-        if (!device) {
-            return Errorf("Sensor device not found.");
-        }
-        auto it = device->sensorsByAbsCode.find(absCode);
-        if (it == device->sensorsByAbsCode.end()) {
-            return Errorf("Sensor map not found.");
-        }
-        const SensorInfo& info = it->second;
-        return std::make_pair(info.sensorType, info.sensorDataIndex);
-    }
-
-    void setExcludedDevices(const std::vector<std::string>& devices) override {
-        mExcludedDevices = devices;
-    }
-
-    std::vector<RawEvent> getEvents(int) override {
-        std::scoped_lock lock(mLock);
-
-        std::vector<RawEvent> buffer;
-        std::swap(buffer, mEvents);
-
-        mEventsCondition.notify_all();
-        return buffer;
-    }
-
-    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
-        auto it = mVideoFrames.find(deviceId);
-        if (it != mVideoFrames.end()) {
-            std::vector<TouchVideoFrame> frames = std::move(it->second);
-            mVideoFrames.erase(deviceId);
-            return frames;
-        }
-        return {};
-    }
-
-    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
-            if (index >= 0) {
-                return device->scanCodeStates.valueAt(index);
-            }
-        }
-        return AKEY_STATE_UNKNOWN;
-    }
-
-    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            return device->countryCode;
-        }
-        return InputDeviceCountryCode::INVALID;
-    }
-
-    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
-            if (index >= 0) {
-                return device->keyCodeStates.valueAt(index);
-            }
-        }
-        return AKEY_STATE_UNKNOWN;
-    }
-
-    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->switchStates.indexOfKey(sw);
-            if (index >= 0) {
-                return device->switchStates.valueAt(index);
-            }
-        }
-        return AKEY_STATE_UNKNOWN;
-    }
-
-    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
-                                  int32_t* outValue) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
-            if (index >= 0) {
-                *outValue = device->absoluteAxisValue.valueAt(index);
-                return OK;
-            }
-        }
-        *outValue = 0;
-        return -1;
-    }
-
-    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
-        Device* device = getDevice(deviceId);
-        if (!device) {
-            return AKEYCODE_UNKNOWN;
-        }
-        auto it = device->keyCodeMapping.find(locationKeyCode);
-        return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
-    }
-
-    // Return true if the device has non-empty key layout.
-    bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
-                               uint8_t* outFlags) const override {
-        bool result = false;
-        Device* device = getDevice(deviceId);
-        if (device) {
-            result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
-            for (size_t i = 0; i < keyCodes.size(); i++) {
-                for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
-                    if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
-                        outFlags[i] = 1;
-                    }
-                }
-                for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
-                    if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
-                        outFlags[i] = 1;
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
-            return index >= 0;
-        }
-        return false;
-    }
-
-    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
-        Device* device = getDevice(deviceId);
-        if (!device) {
-            return false;
-        }
-        for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
-            if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
-                return true;
-            }
-        }
-        for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
-            if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool hasLed(int32_t deviceId, int32_t led) const override {
-        Device* device = getDevice(deviceId);
-        return device && device->leds.indexOfKey(led) >= 0;
-    }
-
-    void setLedState(int32_t deviceId, int32_t led, bool on) override {
-        Device* device = getDevice(deviceId);
-        if (device) {
-            ssize_t index = device->leds.indexOfKey(led);
-            if (index >= 0) {
-                device->leds.replaceValueAt(led, on);
-            } else {
-                ADD_FAILURE()
-                        << "Attempted to set the state of an LED that the EventHub declared "
-                        "was not present.  led=" << led;
-            }
-        }
-    }
-
-    void getVirtualKeyDefinitions(
-            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
-        outVirtualKeys.clear();
-
-        Device* device = getDevice(deviceId);
-        if (device) {
-            outVirtualKeys = device->virtualKeys;
-        }
-    }
-
-    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
-        return nullptr;
-    }
-
-    bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
-        return false;
-    }
-
-    void vibrate(int32_t, const VibrationElement&) override {}
-
-    void cancelVibrate(int32_t) override {}
-
-    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return mVibrators; };
-
-    std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
-        return BATTERY_CAPACITY;
-    }
-
-    std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
-        return BATTERY_STATUS;
-    }
-
-    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override {
-        return {DEFAULT_BATTERY};
-    }
-
-    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
-                                                    int32_t batteryId) const override {
-        if (batteryId != DEFAULT_BATTERY) return {};
-        static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
-                                                        .name = "default battery",
-                                                        .flags = InputBatteryClass::CAPACITY,
-                                                        .path = BATTERY_DEVPATH};
-        return BATTERY_INFO;
-    }
-
-    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override {
-        std::vector<int32_t> ids;
-        for (const auto& [rawId, info] : mRawLightInfos) {
-            ids.push_back(rawId);
-        }
-        return ids;
-    }
-
-    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
-        auto it = mRawLightInfos.find(lightId);
-        if (it == mRawLightInfos.end()) {
-            return std::nullopt;
-        }
-        return it->second;
-    }
-
-    void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override {
-        mLightBrightness.emplace(lightId, brightness);
-    }
-
-    void setLightIntensities(int32_t deviceId, int32_t lightId,
-                             std::unordered_map<LightColor, int32_t> intensities) override {
-        mLightIntensities.emplace(lightId, intensities);
-    };
-
-    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
-        auto lightIt = mLightBrightness.find(lightId);
-        if (lightIt == mLightBrightness.end()) {
-            return std::nullopt;
-        }
-        return lightIt->second;
-    }
-
-    std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
-            int32_t deviceId, int32_t lightId) const override {
-        auto lightIt = mLightIntensities.find(lightId);
-        if (lightIt == mLightIntensities.end()) {
-            return std::nullopt;
-        }
-        return lightIt->second;
-    };
-
-    void dump(std::string&) const override {}
-
-    void monitor() const override {}
-
-    void requestReopenDevices() override {}
-
-    void wake() override {}
-};
-
 // --- FakeInputMapper ---
 
 class FakeInputMapper : public InputMapper {
@@ -1304,117 +344,6 @@
     }
 };
 
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
-    std::queue<std::shared_ptr<InputDevice>> mNextDevices;
-
-public:
-    InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
-                            const sp<InputReaderPolicyInterface>& policy,
-                            InputListenerInterface& listener)
-          : InputReader(eventHub, policy, listener), mFakeContext(this) {}
-
-    virtual ~InstrumentedInputReader() {}
-
-    void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
-
-    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
-                                           const std::string& location = "") {
-        InputDeviceIdentifier identifier;
-        identifier.name = name;
-        identifier.location = location;
-        int32_t generation = deviceId + 1;
-        return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
-    }
-
-    // Make the protected loopOnce method accessible to tests.
-    using InputReader::loopOnce;
-
-protected:
-    virtual std::shared_ptr<InputDevice> createDeviceLocked(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(eventHubId, identifier);
-    }
-
-    // --- FakeInputReaderContext ---
-    class FakeInputReaderContext : public ContextImpl {
-        int32_t mGlobalMetaState;
-        bool mUpdateGlobalMetaStateWasCalled;
-        int32_t mGeneration;
-        std::optional<nsecs_t> mRequestedTimeout;
-        std::vector<InputDeviceInfo> mExternalStylusDevices;
-
-    public:
-        FakeInputReaderContext(InputReader* reader)
-              : ContextImpl(reader),
-                mGlobalMetaState(0),
-                mUpdateGlobalMetaStateWasCalled(false),
-                mGeneration(1) {}
-
-        virtual ~FakeInputReaderContext() {}
-
-        void assertUpdateGlobalMetaStateWasCalled() {
-            ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
-                    << "Expected updateGlobalMetaState() to have been called.";
-            mUpdateGlobalMetaStateWasCalled = false;
-        }
-
-        void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
-
-        uint32_t getGeneration() { return mGeneration; }
-
-        void updateGlobalMetaState() override {
-            mUpdateGlobalMetaStateWasCalled = true;
-            ContextImpl::updateGlobalMetaState();
-        }
-
-        int32_t getGlobalMetaState() override {
-            return mGlobalMetaState | ContextImpl::getGlobalMetaState();
-        }
-
-        int32_t bumpGeneration() override {
-            mGeneration = ContextImpl::bumpGeneration();
-            return mGeneration;
-        }
-
-        void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
-
-        void assertTimeoutWasRequested(nsecs_t when) {
-            ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
-                                           << " but there was no timeout requested.";
-            ASSERT_EQ(when, *mRequestedTimeout);
-            mRequestedTimeout.reset();
-        }
-
-        void assertTimeoutWasNotRequested() {
-            ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
-                                               " but one was requested at time "
-                                            << *mRequestedTimeout;
-        }
-
-        void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
-            outDevices = mExternalStylusDevices;
-        }
-
-        void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
-            mExternalStylusDevices = devices;
-        }
-    } mFakeContext;
-
-    friend class InputReaderTest;
-
-public:
-    FakeInputReaderContext* getContext() { return &mFakeContext; }
-};
-
 // --- InputReaderPolicyTest ---
 class InputReaderPolicyTest : public testing::Test {
 protected:
@@ -1440,9 +369,8 @@
     ASSERT_FALSE(internalViewport);
 
     // Add an internal viewport, then clear it
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT,
-                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, uniqueId, NO_PORT, ViewportType::INTERNAL);
 
     // Check matching by uniqueId
     internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
@@ -1471,21 +399,21 @@
     constexpr int32_t virtualDisplayId2 = 3;
 
     // Add an internal viewport
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId,
-                                    NO_PORT, ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, internalUniqueId, NO_PORT,
+                                    ViewportType::INTERNAL);
     // Add an external viewport
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId,
-                                    NO_PORT, ViewportType::EXTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, externalUniqueId, NO_PORT,
+                                    ViewportType::EXTERNAL);
     // Add an virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1,
-                                    NO_PORT, ViewportType::VIRTUAL);
+                                    ui::ROTATION_0, true /*isActive*/, virtualUniqueId1, NO_PORT,
+                                    ViewportType::VIRTUAL);
     // Add another virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2,
-                                    NO_PORT, ViewportType::VIRTUAL);
+                                    ui::ROTATION_0, true /*isActive*/, virtualUniqueId2, NO_PORT,
+                                    ViewportType::VIRTUAL);
 
     // Check matching by type for internal
     std::optional<DisplayViewport> internalViewport =
@@ -1533,13 +461,11 @@
     for (const ViewportType& type : types) {
         mFakePolicy->clearViewports();
         // Add a viewport
-        mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                        DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1,
-                                        NO_PORT, type);
+        mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                        true /*isActive*/, uniqueId1, NO_PORT, type);
         // Add another viewport
-        mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                        DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2,
-                                        NO_PORT, type);
+        mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                        true /*isActive*/, uniqueId2, NO_PORT, type);
 
         // Check that correct display viewport was returned by comparing the display IDs.
         std::optional<DisplayViewport> viewport1 =
@@ -1579,10 +505,10 @@
     // Add the default display first and ensure it gets returned.
     mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+                                    ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
                                     ViewportType::INTERNAL);
     mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+                                    ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
                                     ViewportType::INTERNAL);
 
     std::optional<DisplayViewport> viewport =
@@ -1594,10 +520,10 @@
     // Add the default display second to make sure order doesn't matter.
     mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+                                    ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
                                     ViewportType::INTERNAL);
     mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+                                    ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
                                     ViewportType::INTERNAL);
 
     viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
@@ -1621,13 +547,11 @@
 
     mFakePolicy->clearViewports();
     // Add a viewport that's associated with some display port that's not of interest.
-    mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3,
-                                    type);
+    mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, uniqueId1, hdmi3, type);
     // Add another viewport, connected to HDMI1 port
-    mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1,
-                                    type);
+    mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, uniqueId2, hdmi1, type);
 
     // Check that correct display viewport was returned by comparing the display ports.
     std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
@@ -2080,11 +1004,10 @@
 
     // Add default and second display.
     mFakePolicy->clearViewports();
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT,
-                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, "local:0", NO_PORT, ViewportType::INTERNAL);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1,
+                                    ui::ROTATION_0, true /*isActive*/, "local:1", hdmi1,
                                     ViewportType::EXTERNAL);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
@@ -2283,8 +1206,9 @@
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
-    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
-    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+    ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY),
+              FakeEventHub::BATTERY_CAPACITY);
+    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), FakeEventHub::BATTERY_CAPACITY);
 }
 
 TEST_F(InputReaderTest, BatteryGetStatus) {
@@ -2300,8 +1224,9 @@
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
-    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
-    ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+    ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY),
+              FakeEventHub::BATTERY_STATUS);
+    ASSERT_EQ(mReader->getBatteryStatus(deviceId), FakeEventHub::BATTERY_STATUS);
 }
 
 TEST_F(InputReaderTest, BatteryGetDevicePath) {
@@ -2316,7 +1241,7 @@
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
-    ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), BATTERY_DEVPATH);
+    ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), FakeEventHub::BATTERY_DEVPATH);
 }
 
 TEST_F(InputReaderTest, LightGetColor) {
@@ -2541,9 +1466,8 @@
 #endif
         InputReaderIntegrationTest::SetUp();
         // At least add an internal display.
-        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                     DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
-                                     ViewportType::INTERNAL);
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                     UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
 
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2554,7 +1478,7 @@
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-                                      int32_t orientation, const std::string& uniqueId,
+                                      ui::Rotation orientation, const std::string& uniqueId,
                                       std::optional<uint8_t> physicalPort,
                                       ViewportType viewportType) {
         mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
@@ -3492,7 +2416,7 @@
 
     // Prepare displays.
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
+                                    ui::ROTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
                                     ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                  InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3527,7 +2451,7 @@
 
     // Device should be enabled when a display is found.
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+                                    ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                  InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3553,7 +2477,7 @@
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+                                    ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
     unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                  InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3581,194 +2505,6 @@
     ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
 }
 
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
-    static const char* DEVICE_NAME;
-    static const char* DEVICE_LOCATION;
-    static const int32_t DEVICE_ID;
-    static const int32_t DEVICE_GENERATION;
-    static const int32_t DEVICE_CONTROLLER_NUMBER;
-    static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
-    static const int32_t EVENTHUB_ID;
-
-    std::shared_ptr<FakeEventHub> mFakeEventHub;
-    sp<FakeInputReaderPolicy> mFakePolicy;
-    std::unique_ptr<TestInputListener> mFakeListener;
-    std::unique_ptr<InstrumentedInputReader> mReader;
-    std::shared_ptr<InputDevice> mDevice;
-
-    virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0) {
-        mFakeEventHub = std::make_unique<FakeEventHub>();
-        mFakePolicy = sp<FakeInputReaderPolicy>::make();
-        mFakeListener = std::make_unique<TestInputListener>();
-        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            *mFakeListener);
-        mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
-        // Consume the device reset notification generated when adding a new device.
-        mFakeListener->assertNotifyDeviceResetWasCalled();
-    }
-
-    void SetUp() override {
-        SetUp(DEVICE_CLASSES);
-    }
-
-    void TearDown() override {
-        mFakeListener.reset();
-        mFakePolicy.clear();
-    }
-
-    void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
-    }
-
-    std::list<NotifyArgs> configureDevice(uint32_t changes) {
-        if (!changes ||
-            (changes &
-             (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-              InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
-            mReader->requestRefreshConfiguration(changes);
-            mReader->loopOnce();
-        }
-        std::list<NotifyArgs> out =
-                mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
-        // Loop the reader to flush the input listener queue.
-        for (const NotifyArgs& args : out) {
-            mFakeListener->notify(args);
-        }
-        mReader->loopOnce();
-        return out;
-    }
-
-    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
-                                           const std::string& location, int32_t eventHubId,
-                                           ftl::Flags<InputDeviceClass> classes, int bus = 0) {
-        InputDeviceIdentifier identifier;
-        identifier.name = name;
-        identifier.location = location;
-        identifier.bus = bus;
-        std::shared_ptr<InputDevice> device =
-                std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
-                                              identifier);
-        mReader->pushNextDevice(device);
-        mFakeEventHub->addDevice(eventHubId, name, classes, bus);
-        mReader->loopOnce();
-        return device;
-    }
-
-    template <class T, typename... Args>
-    T& addMapperAndConfigure(Args... args) {
-        T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
-        configureDevice(0);
-        std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
-        resetArgList += mapper.reset(ARBITRARY_TIME);
-        // Loop the reader to flush the input listener queue.
-        for (const NotifyArgs& loopArgs : resetArgList) {
-            mFakeListener->notify(loopArgs);
-        }
-        mReader->loopOnce();
-        return mapper;
-    }
-
-    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId,
-            std::optional<uint8_t> physicalPort, ViewportType viewportType) {
-        mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
-                                        uniqueId, physicalPort, viewportType);
-        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-    }
-
-    void clearViewports() {
-        mFakePolicy->clearViewports();
-    }
-
-    std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
-                                  int32_t code, int32_t value) {
-        RawEvent event;
-        event.when = when;
-        event.readTime = readTime;
-        event.deviceId = mapper.getDeviceContext().getEventHubId();
-        event.type = type;
-        event.code = code;
-        event.value = value;
-        std::list<NotifyArgs> processArgList = mapper.process(&event);
-        for (const NotifyArgs& args : processArgList) {
-            mFakeListener->notify(args);
-        }
-        // Loop the reader to flush the input listener queue.
-        mReader->loopOnce();
-        return processArgList;
-    }
-
-    void resetMapper(InputMapper& mapper, nsecs_t when) {
-        const auto resetArgs = mapper.reset(when);
-        for (const auto args : resetArgs) {
-            mFakeListener->notify(args);
-        }
-        // Loop the reader to flush the input listener queue.
-        mReader->loopOnce();
-    }
-
-    std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
-        std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
-        for (const NotifyArgs& args : generatedArgs) {
-            mFakeListener->notify(args);
-        }
-        // Loop the reader to flush the input listener queue.
-        mReader->loopOnce();
-        return generatedArgs;
-    }
-
-    static void assertMotionRange(const InputDeviceInfo& info,
-            int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
-        const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
-        ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
-        ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
-        ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
-        ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
-        ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
-        ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
-        ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
-    }
-
-    static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
-                                    float size, float touchMajor, float touchMinor, float toolMajor,
-                                    float toolMinor, float orientation, float distance,
-                                    float scaledAxisEpsilon = 1.f) {
-        ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
-        ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
-        ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
-        ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
-        ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                    scaledAxisEpsilon);
-        ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                    scaledAxisEpsilon);
-        ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                    scaledAxisEpsilon);
-        ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                    scaledAxisEpsilon);
-        ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
-        ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
-    }
-
-    static void assertPosition(const FakePointerController& controller, float x, float y) {
-        float actualX, actualY;
-        controller.getPosition(&actualX, &actualY);
-        ASSERT_NEAR(x, actualX, 1);
-        ASSERT_NEAR(y, actualY, 1);
-    }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
-        ftl::Flags<InputDeviceClass>(0); // not needed for current tests
-const int32_t InputMapperTest::EVENTHUB_ID = 1;
-
 // --- SwitchInputMapperTest ---
 
 class SwitchInputMapperTest : public InputMapperTest {
@@ -4028,7 +2764,7 @@
 protected:
     const std::string UNIQUE_ID = "local:0";
 
-    void prepareDisplay(int32_t orientation);
+    void prepareDisplay(ui::Rotation orientation);
 
     void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
                              int32_t originalKeyCode, int32_t rotatedKeyCode,
@@ -4038,7 +2774,7 @@
 /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
  * orientation.
  */
-void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
                                  NO_PORT, ViewportType::INTERNAL);
 }
@@ -4176,6 +2912,27 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 }
 
+TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
+    mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    // Key down by scan code.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AKEYCODE_B, args.keyCode);
+
+    // Key up by scan code.
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AKEYCODE_B, args.keyCode);
+}
+
 /**
  * Ensure that the readTime is set to the time when the EV_KEY is received.
  */
@@ -4250,7 +3007,7 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -4272,7 +3029,7 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     ASSERT_NO_FATAL_FAILURE(
             testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4283,7 +3040,7 @@
                                                 AKEYCODE_DPAD_LEFT, DISPLAY_ID));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(
             testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4294,7 +3051,7 @@
                                                 AKEYCODE_DPAD_DOWN, DISPLAY_ID));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_180);
+    prepareDisplay(ui::ROTATION_180);
     ASSERT_NO_FATAL_FAILURE(
             testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4305,7 +3062,7 @@
                                                 AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
+    prepareDisplay(ui::ROTATION_270);
     ASSERT_NO_FATAL_FAILURE(
             testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4319,7 +3076,7 @@
     // in the key up as we did in the key down.
     NotifyKeyArgs args;
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
+    prepareDisplay(ui::ROTATION_270);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
@@ -4327,7 +3084,7 @@
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_180);
+    prepareDisplay(ui::ROTATION_180);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -4352,7 +3109,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
@@ -4374,7 +3131,7 @@
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
     // ^--- already checked by the previous test
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                  UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -4384,7 +3141,7 @@
 
     constexpr int32_t newDisplayId = 2;
     clearViewports();
-    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                  UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -4594,9 +3351,9 @@
 
     // Prepare second display.
     constexpr int32_t newDisplayId = 2;
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                  UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
-    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
                                  SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
     // Default device will reconfigure above, need additional reconfiguration for another device.
     unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
@@ -4927,14 +3684,14 @@
     void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
                             int32_t rotatedX, int32_t rotatedY);
 
-    void prepareDisplay(int32_t orientation) {
+    void prepareDisplay(ui::Rotation orientation) {
         setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
                                      DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     }
 
     void prepareSecondaryDisplay() {
         setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                     DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+                                     ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
                                      ViewportType::EXTERNAL);
     }
 
@@ -5219,7 +3976,7 @@
     addConfigurationProperty("cursor.orientationAware", "1");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -5238,7 +3995,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -5249,7 +4006,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -5260,7 +4017,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_180);
+    prepareDisplay(ui::ROTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -5271,7 +4028,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
+    prepareDisplay(ui::ROTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -5735,7 +4492,7 @@
     ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
 
     // Ensure the display is rotated.
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
 
     NotifyMotionArgs args;
 
@@ -5771,7 +4528,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     // Set up the default display.
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
 
     // Set up the secondary display as the display on which the pointer should be shown.
     // The InputDevice is not associated with any display.
@@ -5798,7 +4555,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     // Set up the default display.
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
 
     // Set up the secondary display as the display on which the pointer should be shown,
     // and associate the InputDevice with the secondary display.
@@ -5825,7 +4582,7 @@
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     // Set up the default display as the display on which the pointer should be shown.
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
 
     // Associate the InputDevice with the secondary display.
@@ -5992,9 +4749,9 @@
         TOOL_TYPE = 1 << 10,
     };
 
-    void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+    void prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port = NO_PORT);
     void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
-    void prepareVirtualDisplay(int32_t orientation);
+    void prepareVirtualDisplay(ui::Rotation orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
@@ -6048,17 +4805,17 @@
         { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
 };
 
-void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
+void TouchInputMapperTest::prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
                                  port, ViewportType::INTERNAL);
 }
 
 void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
     setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
+                                 ui::ROTATION_0, SECONDARY_UNIQUE_ID, port, type);
 }
 
-void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareVirtualDisplay(ui::Rotation orientation) {
     setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
                                  orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
                                  ViewportType::VIRTUAL);
@@ -6225,7 +4982,7 @@
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6253,7 +5010,7 @@
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6281,7 +5038,7 @@
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6296,7 +5053,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6346,7 +5103,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6467,7 +5224,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6542,7 +5299,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
 
-    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+    prepareVirtualDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6638,7 +5395,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
@@ -6737,7 +5494,7 @@
     NotifyMotionArgs args;
 
     // Rotation 90.
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -6763,7 +5520,7 @@
 
     // Rotation 0.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -6777,7 +5534,7 @@
 
     // Rotation 90.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
@@ -6791,7 +5548,7 @@
 
     // Rotation 180.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_180);
+    prepareDisplay(ui::ROTATION_180);
     processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
     processSync(mapper);
 
@@ -6805,7 +5562,7 @@
 
     // Rotation 270.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
+    prepareDisplay(ui::ROTATION_270);
     processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
 
@@ -6825,7 +5582,7 @@
     addConfigurationProperty("touch.orientationAware", "1");
     addConfigurationProperty("touch.orientation", "ORIENTATION_0");
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
@@ -6849,7 +5606,7 @@
     addConfigurationProperty("touch.orientationAware", "1");
     addConfigurationProperty("touch.orientation", "ORIENTATION_90");
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
@@ -6873,7 +5630,7 @@
     addConfigurationProperty("touch.orientationAware", "1");
     addConfigurationProperty("touch.orientation", "ORIENTATION_180");
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
@@ -6897,7 +5654,7 @@
     addConfigurationProperty("touch.orientationAware", "1");
     addConfigurationProperty("touch.orientation", "ORIENTATION_270");
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
@@ -6928,7 +5685,7 @@
 
     // Orientation 90, Rotation 0.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
     processSync(mapper);
 
@@ -6942,7 +5699,7 @@
 
     // Orientation 90, Rotation 90.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
     processSync(mapper);
 
@@ -6956,7 +5713,7 @@
 
     // Orientation 90, Rotation 180.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_180);
+    prepareDisplay(ui::ROTATION_180);
     processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
@@ -6970,7 +5727,7 @@
 
     // Orientation 90, Rotation 270.
     clearViewports();
-    prepareDisplay(DISPLAY_ORIENTATION_270);
+    prepareDisplay(ui::ROTATION_270);
     processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
                 RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
     processSync(mapper);
@@ -6986,7 +5743,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7030,7 +5787,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareLocationCalibration();
     prepareButtons();
     prepareAxes(POSITION);
@@ -7053,7 +5810,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7296,7 +6053,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7431,7 +6188,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
@@ -7503,7 +6260,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7574,7 +6331,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7596,7 +6353,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7624,7 +6381,7 @@
 TEST_F(SingleTouchInputMapperTest,
        Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7652,7 +6409,7 @@
 TEST_F(SingleTouchInputMapperTest,
        Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7712,7 +6469,7 @@
 
 TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7755,27 +6512,25 @@
     // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
     // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
     // rotated equivalent of the given un-rotated physical display bounds.
-    void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+    void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
         uint32_t inverseRotationFlags;
         auto width = DISPLAY_WIDTH;
         auto height = DISPLAY_HEIGHT;
         switch (orientation) {
-            case DISPLAY_ORIENTATION_90:
+            case ui::ROTATION_90:
                 inverseRotationFlags = ui::Transform::ROT_270;
                 std::swap(width, height);
                 break;
-            case DISPLAY_ORIENTATION_180:
+            case ui::ROTATION_180:
                 inverseRotationFlags = ui::Transform::ROT_180;
                 break;
-            case DISPLAY_ORIENTATION_270:
+            case ui::ROTATION_270:
                 inverseRotationFlags = ui::Transform::ROT_90;
                 std::swap(width, height);
                 break;
-            case DISPLAY_ORIENTATION_0:
+            case ui::ROTATION_0:
                 inverseRotationFlags = ui::Transform::ROT_0;
                 break;
-            default:
-                FAIL() << "Invalid orientation: " << orientation;
         }
 
         const ui::Transform rotation(inverseRotationFlags, width, height);
@@ -7819,7 +6574,7 @@
 
 TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
 
     prepareButtons();
     prepareAxes(POSITION);
@@ -7834,8 +6589,7 @@
     static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
             {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
 
-    for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
-                             DISPLAY_ORIENTATION_270}) {
+    for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
         configurePhysicalDisplay(orientation, kPhysicalDisplay);
 
         // Touches outside the physical display should be ignored, and should not generate any
@@ -7855,7 +6609,7 @@
 
 TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
 
     prepareButtons();
     prepareAxes(POSITION);
@@ -7868,8 +6622,7 @@
     // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
     static const Rect kPhysicalDisplay{10, 20, 70, 160};
 
-    for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
-                             DISPLAY_ORIENTATION_270}) {
+    for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
         configurePhysicalDisplay(orientation, kPhysicalDisplay);
 
         // Touches that start outside the physical display should be ignored until it enters the
@@ -7920,7 +6673,7 @@
 public:
     SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
         addConfigurationProperty("touch.deviceType", "touchScreen");
-        prepareDisplay(DISPLAY_ORIENTATION_0);
+        prepareDisplay(ui::ROTATION_0);
         prepareButtons();
         prepareAxes(POSITION);
         auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -8409,7 +7162,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8681,7 +7434,7 @@
 
 TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
 
     mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
                                    /*fuzz*/ 0, /*resolution*/ 10);
@@ -8711,7 +7464,7 @@
 
 TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
 
     mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
                                    /*fuzz*/ 0, /*resolution*/ 10);
@@ -8732,7 +7485,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8903,7 +7656,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9069,7 +7822,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9118,7 +7871,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.size.calibration", "geometric");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9155,7 +7908,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.size.calibration", "diameter");
     addConfigurationProperty("touch.size.scale", "10");
@@ -9206,7 +7959,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.size.calibration", "area");
     addConfigurationProperty("touch.size.scale", "43");
@@ -9239,7 +7992,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | PRESSURE);
     addConfigurationProperty("touch.pressure.calibration", "amplitude");
     addConfigurationProperty("touch.pressure.scale", "0.01");
@@ -9273,7 +8026,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9516,7 +8269,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9573,7 +8326,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9723,7 +8476,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9794,7 +8547,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9894,7 +8647,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // Add viewport for display 1 on hdmi1
-    prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+    prepareDisplay(ui::ROTATION_0, hdmi1);
     // Send a touch event again
     processPosition(mapper, 100, 100);
     processSync(mapper);
@@ -9911,8 +8664,8 @@
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
+    prepareVirtualDisplay(ui::ROTATION_0);
 
     // Send a touch event
     processPosition(mapper, 100, 100);
@@ -9935,7 +8688,7 @@
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     prepareSecondaryDisplay(ViewportType::EXTERNAL);
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -9959,7 +8712,7 @@
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
     process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
     process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
@@ -9984,9 +8737,8 @@
 TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     // Don't set touch.enableForInactiveViewport to verify the default behavior.
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
-                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10005,9 +8757,8 @@
 TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.enableForInactiveViewport", "1");
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
-                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10023,9 +8774,8 @@
 TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.enableForInactiveViewport", "0");
-    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                                    DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
-                                    ViewportType::INTERNAL);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     std::optional<DisplayViewport> optionalDisplayViewport =
             mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
     ASSERT_TRUE(optionalDisplayViewport.has_value());
@@ -10119,7 +8869,7 @@
     mFakePolicy->setShowTouches(true);
 
     // Create displays.
-    prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+    prepareDisplay(ui::ROTATION_0, hdmi1);
     prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
 
     // Default device will reconfigure above, need additional reconfiguration for another device.
@@ -10164,7 +8914,7 @@
 TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
@@ -10195,8 +8945,7 @@
     NotifyMotionArgs motionArgs;
 
     // Test all 4 orientations
-    for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
-                                DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+    for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
         SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
         clearViewports();
         prepareDisplay(orientation);
@@ -10221,8 +8970,7 @@
     NotifyMotionArgs motionArgs;
 
     // Test all 4 orientations
-    for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
-             DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+    for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
         SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
         clearViewports();
         prepareDisplay(orientation);
@@ -10256,7 +9004,7 @@
     std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
     NotifyMotionArgs motionArgs;
 
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
@@ -10279,7 +9027,7 @@
     std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
     NotifyMotionArgs motionArgs;
 
-    prepareDisplay(DISPLAY_ORIENTATION_90);
+    prepareDisplay(ui::ROTATION_90);
     mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
@@ -10289,7 +9037,7 @@
         // compared to the display. This is so that when the window transform (which contains the
         // display rotation) is applied later by InputDispatcher, the coordinates end up in the
         // window's coordinate space.
-        frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+        frame.rotate(getInverseRotation(ui::ROTATION_90));
     });
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
@@ -10326,7 +9074,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10371,7 +9119,7 @@
  */
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10419,7 +9167,7 @@
  */
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10494,7 +9242,7 @@
  */
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10592,7 +9340,7 @@
  */
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10664,7 +9412,7 @@
  */
 TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10721,7 +9469,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10762,7 +9510,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -10790,7 +9538,7 @@
 
 TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
@@ -10849,7 +9597,7 @@
 TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
@@ -10880,7 +9628,7 @@
     fakePointerController->setButtonState(0);
 
     // prepare device and capture
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -11030,7 +9778,7 @@
     fakePointerController->setButtonState(0);
 
     // prepare device and capture
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -11071,7 +9819,7 @@
     std::shared_ptr<FakePointerController> fakePointerController =
             std::make_shared<FakePointerController>();
 
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
@@ -11098,7 +9846,7 @@
 
 TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
@@ -11148,7 +9896,7 @@
         fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
         fakePointerController->setPosition(0, 0);
         fakePointerController->setButtonState(0);
-        prepareDisplay(DISPLAY_ORIENTATION_0);
+        prepareDisplay(ui::ROTATION_0);
 
         prepareAxes(POSITION);
         prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
@@ -11508,7 +10256,7 @@
         process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     }
 
-    void prepareVirtualDisplay(int32_t orientation) {
+    void prepareVirtualDisplay(ui::Rotation orientation) {
         setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
                                      VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
                                      NO_PORT, ViewportType::VIRTUAL);
@@ -11526,7 +10274,7 @@
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
 
-    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+    prepareVirtualDisplay(ui::ROTATION_0);
 
     // Send an axis event
     processAxis(mapper, ABS_X, 100);
@@ -11629,15 +10377,17 @@
 TEST_F(BatteryControllerTest, GetBatteryCapacity) {
     PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
 
-    ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
-    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+    ASSERT_TRUE(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+              FakeEventHub::BATTERY_CAPACITY);
 }
 
 TEST_F(BatteryControllerTest, GetBatteryStatus) {
     PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
 
-    ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
-    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+    ASSERT_TRUE(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+              FakeEventHub::BATTERY_STATUS);
 }
 
 // --- LightControllerTest ---
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
new file mode 100644
index 0000000..1f8cd12
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 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 "InstrumentedInputReader.h"
+
+namespace android {
+
+InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+                                                 const sp<InputReaderPolicyInterface>& policy,
+                                                 InputListenerInterface& listener)
+      : InputReader(eventHub, policy, listener), mFakeContext(this) {}
+
+void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) {
+    mNextDevices.push(device);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId,
+                                                                const std::string& name,
+                                                                const std::string& location) {
+    InputDeviceIdentifier identifier;
+    identifier.name = name;
+    identifier.location = location;
+    int32_t generation = deviceId + 1;
+    return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
+        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(eventHubId, identifier);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
new file mode 100644
index 0000000..7f8d556
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2022 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 <memory>
+#include <queue>
+#include <string>
+
+#include <InputDevice.h>
+#include <InputReader.h>
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class InstrumentedInputReader : public InputReader {
+public:
+    InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+                            const sp<InputReaderPolicyInterface>& policy,
+                            InputListenerInterface& listener);
+    virtual ~InstrumentedInputReader() {}
+
+    void pushNextDevice(std::shared_ptr<InputDevice> device);
+
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location = "");
+
+    // Make the protected loopOnce method accessible to tests.
+    using InputReader::loopOnce;
+
+protected:
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(
+            int32_t eventHubId, const InputDeviceIdentifier& identifier);
+
+    class FakeInputReaderContext : public ContextImpl {
+    public:
+        FakeInputReaderContext(InputReader* reader)
+              : ContextImpl(reader),
+                mGlobalMetaState(0),
+                mUpdateGlobalMetaStateWasCalled(false),
+                mGeneration(1) {}
+
+        virtual ~FakeInputReaderContext() {}
+
+        void assertUpdateGlobalMetaStateWasCalled() {
+            ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+                    << "Expected updateGlobalMetaState() to have been called.";
+            mUpdateGlobalMetaStateWasCalled = false;
+        }
+
+        void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+        uint32_t getGeneration() { return mGeneration; }
+
+        void updateGlobalMetaState() override {
+            mUpdateGlobalMetaStateWasCalled = true;
+            ContextImpl::updateGlobalMetaState();
+        }
+
+        int32_t getGlobalMetaState() override {
+            return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+        }
+
+        int32_t bumpGeneration() override {
+            mGeneration = ContextImpl::bumpGeneration();
+            return mGeneration;
+        }
+
+        void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+        void assertTimeoutWasRequested(nsecs_t when) {
+            ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+                                           << " but there was no timeout requested.";
+            ASSERT_EQ(when, *mRequestedTimeout);
+            mRequestedTimeout.reset();
+        }
+
+        void assertTimeoutWasNotRequested() {
+            ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+                                               " but one was requested at time "
+                                            << *mRequestedTimeout;
+        }
+
+        void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+            outDevices = mExternalStylusDevices;
+        }
+
+        void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+            mExternalStylusDevices = devices;
+        }
+
+    private:
+        int32_t mGlobalMetaState;
+        bool mUpdateGlobalMetaStateWasCalled;
+        int32_t mGeneration;
+        std::optional<nsecs_t> mRequestedTimeout;
+        std::vector<InputDeviceInfo> mExternalStylusDevices;
+    } mFakeContext;
+
+    friend class InputReaderTest;
+
+public:
+    FakeInputReaderContext* getContext() { return &mFakeContext; }
+
+private:
+    std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp
new file mode 100644
index 0000000..6715585
--- /dev/null
+++ b/services/inputflinger/tests/NotifyArgs_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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 <NotifyArgs.h>
+#include <utils/Timers.h>
+
+#include <gtest/gtest.h>
+#include "android/input.h"
+#include "input/Input.h"
+#include "input/TouchVideoFrame.h"
+
+namespace android {
+
+// --- NotifyArgsTest ---
+
+/**
+ * Validate basic copy assignment.
+ */
+TEST(NotifyMotionArgsTest, TestCopyAssignmentOperator) {
+    int32_t id = 123;
+    nsecs_t downTime = systemTime();
+    nsecs_t eventTime = downTime++;
+    nsecs_t readTime = downTime++;
+    int32_t deviceId = 7;
+    uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    int32_t displayId = 42;
+    uint32_t policyFlags = POLICY_FLAG_GESTURE;
+    int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE;
+    int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY;
+    int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+    int32_t metaState = AMETA_SCROLL_LOCK_ON;
+    uint32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY;
+    MotionClassification classification = MotionClassification::DEEP_PRESS;
+    int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+    uint32_t pointerCount = 2;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    float x = 0;
+    float y = 10;
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x++);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y++);
+    }
+
+    float xPrecision = 1.2f;
+    float yPrecision = 3.4f;
+    float xCursorPosition = 5.6f;
+    float yCursorPosition = 7.8f;
+
+    std::vector<int16_t> videoData = {1, 2, 3, 4};
+    timeval timestamp = {5, 6};
+    TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+    std::vector<TouchVideoFrame> videoFrames = {frame};
+    const NotifyMotionArgs args(id, eventTime, readTime, deviceId, source, displayId, policyFlags,
+                                action, actionButton, flags, metaState, buttonState, classification,
+                                edgeFlags, pointerCount, pointerProperties, pointerCoords,
+                                xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+                                videoFrames);
+
+    NotifyMotionArgs otherArgs{};
+    otherArgs = args;
+
+    EXPECT_EQ(args, otherArgs);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
new file mode 100644
index 0000000..27881f6
--- /dev/null
+++ b/services/inputflinger/tests/TestConstants.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 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
+
+namespace android {
+
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
+// An arbitrary time value.
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
+
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 2eed997..057c15d 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -153,6 +153,10 @@
         return reader->getLightPlayerId(deviceId, lightId);
     }
 
+    void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+        reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+    }
+
     int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
         return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
     }
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 64316ba..cd852d6 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -211,6 +211,7 @@
     int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
         return mFdp->ConsumeIntegral<int32_t>();
     }
+    void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
     int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
         return mFdp->ConsumeIntegral<int32_t>();
     }
@@ -311,7 +312,7 @@
         return mFdp->ConsumeRandomLengthString(32);
     }
     TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
-                                                           int32_t surfaceRotation) override {
+                                                           ui::Rotation surfaceRotation) override {
         return mTransform;
     }
     void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 46f00e8..398cdf9 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -87,6 +87,42 @@
 
 // ---------------------------------------------------------------------------
 
+RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback)
+  : BaseSensor(sensor), mCallback(std::move(callback)) {
+}
+
+status_t RuntimeSensor::activate(void*, bool enabled) {
+    if (enabled != mEnabled) {
+        mEnabled = enabled;
+        mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+    }
+    return OK;
+}
+
+status_t RuntimeSensor::batch(void*, int, int, int64_t samplingPeriodNs,
+                              int64_t maxBatchReportLatencyNs) {
+    if (mSamplingPeriodNs != samplingPeriodNs || mBatchReportLatencyNs != maxBatchReportLatencyNs) {
+        mSamplingPeriodNs = samplingPeriodNs;
+        mBatchReportLatencyNs = maxBatchReportLatencyNs;
+        if (mEnabled) {
+            mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+        }
+    }
+    return OK;
+}
+
+status_t RuntimeSensor::setDelay(void*, int, int64_t ns) {
+    if (mSamplingPeriodNs != ns) {
+        mSamplingPeriodNs = ns;
+        if (mEnabled) {
+            mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+        }
+    }
+    return OK;
+}
+
+// ---------------------------------------------------------------------------
+
 ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service)
         : HardwareSensor(sensor), mSensorService(service) {
 }
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 5704359..5ee5e12 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -104,6 +104,32 @@
 
 // ---------------------------------------------------------------------------
 
+class RuntimeSensor : public BaseSensor {
+public:
+    static constexpr int DEFAULT_DEVICE_ID = 0;
+
+    class StateChangeCallback : public virtual RefBase {
+      public:
+        virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+                                    int64_t batchReportLatencyNs) = 0;
+    };
+    RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback);
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+                           int64_t maxBatchReportLatencyNs) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
+    virtual bool process(sensors_event_t*, const sensors_event_t&) { return false; }
+    virtual bool isVirtual() const override { return false; }
+
+private:
+    bool mEnabled = false;
+    int64_t mSamplingPeriodNs = 0;
+    int64_t mBatchReportLatencyNs = 0;
+    sp<StateChangeCallback> mCallback;
+};
+
+// ---------------------------------------------------------------------------
+
 class ProximitySensor : public HardwareSensor {
 public:
     explicit ProximitySensor(const sensor_t& sensor, SensorService& service);
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 85ce0f0..6d36b47 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -29,12 +29,12 @@
 const Sensor SensorList::mNonSensor = Sensor("unknown");
 
 bool SensorList::add(
-        int handle, SensorInterface* si, bool isForDebug, bool isVirtual) {
+        int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) {
     std::lock_guard<std::mutex> lk(mLock);
     if (handle == si->getSensor().getHandle() &&
         mUsedHandle.insert(handle).second) {
         // will succeed as the mUsedHandle does not have this handle
-        mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual));
+        mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId));
         return true;
     }
     // handle exist already or handle mismatch
@@ -79,7 +79,8 @@
     Vector<Sensor> sensors;
     forEachEntry(
             [&sensors] (const Entry& e) -> bool {
-                if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) {
+                if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()
+                    && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
                     sensors.add(e.si->getSensor());
                 }
                 return true;
@@ -92,7 +93,8 @@
     Vector<Sensor> sensors;
     forEachEntry(
             [&sensors] (const Entry& e) -> bool {
-                if (!e.si->getSensor().isDynamicSensor()) {
+                if (!e.si->getSensor().isDynamicSensor()
+                    && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
                     sensors.add(e.si->getSensor());
                 }
                 return true;
@@ -105,7 +107,8 @@
     Vector<Sensor> sensors;
     forEachEntry(
             [&sensors] (const Entry& e) -> bool {
-                if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) {
+                if (!e.isForDebug && e.si->getSensor().isDynamicSensor()
+                     && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
                     sensors.add(e.si->getSensor());
                 }
                 return true;
@@ -118,7 +121,20 @@
     Vector<Sensor> sensors;
     forEachEntry(
             [&sensors] (const Entry& e) -> bool {
-                if (e.isVirtual) {
+                if (e.isVirtual && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+                    sensors.add(e.si->getSensor());
+                }
+                return true;
+            });
+    return sensors;
+}
+
+const Vector<Sensor> SensorList::getRuntimeSensors(int deviceId) const {
+    // lock in forEachEntry
+    Vector<Sensor> sensors;
+    forEachEntry(
+            [&sensors, deviceId] (const Entry& e) -> bool {
+                if (!e.isForDebug && e.deviceId == deviceId) {
                     sensors.add(e.si->getSensor());
                 }
                 return true;
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 049ae7c..79f6701 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -40,14 +40,16 @@
         sp<SensorInterface> si;
         const bool isForDebug;
         const bool isVirtual;
-        Entry(SensorInterface* si_, bool debug_, bool virtual_) :
-            si(si_), isForDebug(debug_), isVirtual(virtual_) {
+        const int deviceId;
+        Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) :
+            si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
         }
     };
 
     // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
     // object it pointed to and the object should not be released elsewhere.
-    bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false);
+    bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false,
+             int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
 
     // After a handle is removed, the object that SensorInterface * pointing to may get deleted if
     // no more sp<> of the same object exist.
@@ -60,6 +62,7 @@
     const Vector<Sensor> getUserDebugSensors() const;
     const Vector<Sensor> getDynamicSensors() const;
     const Vector<Sensor> getVirtualSensors() const;
+    const Vector<Sensor> getRuntimeSensors(int deviceId) const;
 
     String8 getName(int handle) const;
     String8 getStringType(int handle) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 21d6b6b..0c9fef5 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -102,6 +102,33 @@
 static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
 static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS");
 
+namespace {
+
+// TODO(b/259227294): Move the sensor ranges to the HAL.
+int32_t nextRuntimeSensorHandle() {
+    static constexpr int32_t kRuntimeHandleBase = 0x5F000000;
+    static constexpr int32_t kRuntimeHandleEnd = 0x5FFFFFFF;
+    static int32_t nextHandle = kRuntimeHandleBase;
+    if (nextHandle == kRuntimeHandleEnd) {
+        return -1;
+    }
+    return nextHandle++;
+}
+
+class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback {
+ public:
+    RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback)
+        : mCallback(std::move(callback)) {}
+    void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+                        int64_t batchReportLatencyNs) override {
+        mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs);
+    }
+ private:
+    sp<SensorService::RuntimeSensorStateChangeCallback> mCallback;
+};
+
+} // namespace
+
 static bool isAutomotive() {
     sp<IServiceManager> serviceManager = defaultServiceManager();
     if (serviceManager.get() == nullptr) {
@@ -137,6 +164,60 @@
     mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this);
 }
 
+int SensorService::registerRuntimeSensor(
+    const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) {
+    int handle = 0;
+    while (handle == 0 || !mSensors.isNewHandle(handle)) {
+        handle = nextRuntimeSensorHandle();
+        if (handle < 0) {
+            // Ran out of the dedicated range for runtime sensors.
+            return handle;
+        }
+    }
+
+    ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s",
+            handle, sensor.type, sensor.name);
+
+    sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback(
+        new RuntimeSensorCallbackProxy(std::move(callback)));
+    sensor_t runtimeSensor = sensor;
+    // force the handle to be consistent
+    runtimeSensor.handle = handle;
+    SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback));
+
+    Mutex::Autolock _l(mLock);
+    const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId);
+
+    if (s.getHandle() != handle) {
+        // The registration was unsuccessful.
+        return s.getHandle();
+    }
+    return handle;
+}
+
+status_t SensorService::unregisterRuntimeSensor(int handle) {
+    ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+    {
+        Mutex::Autolock _l(mLock);
+        if (!unregisterDynamicSensorLocked(handle)) {
+            ALOGE("Runtime sensor release error.");
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+    for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+        connection->removeSensor(handle);
+    }
+    return OK;
+}
+
+status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) {
+    Mutex::Autolock _l(mLock);
+    mRuntimeSensorEventQueue.push(event);
+    return OK;
+}
+
 bool SensorService::initializeHmacKey() {
     int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC);
     if (fd != -1) {
@@ -407,10 +488,11 @@
         && isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
 }
 
-const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) {
+const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual,
+                                            int deviceId) {
     int handle = s->getSensor().getHandle();
     int type = s->getSensor().getType();
-    if (mSensors.add(handle, s, isDebug, isVirtual)){
+    if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) {
         mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
         return s->getSensor();
     } else {
@@ -1003,6 +1085,7 @@
         recordLastValueLocked(mSensorEventBuffer, count);
 
         // handle virtual sensors
+        bool bufferNeedsSorting = false;
         if (count && vcount) {
             sensors_event_t const * const event = mSensorEventBuffer;
             if (!mActiveVirtualSensors.empty()) {
@@ -1038,12 +1121,37 @@
                     // record the last synthesized values
                     recordLastValueLocked(&mSensorEventBuffer[count], k);
                     count += k;
-                    // sort the buffer by time-stamps
-                    sortEventBuffer(mSensorEventBuffer, count);
+                    bufferNeedsSorting = true;
                 }
             }
         }
 
+        // handle runtime sensors
+        {
+            size_t k = 0;
+            while (!mRuntimeSensorEventQueue.empty()) {
+                if (count + k >= minBufferSize) {
+                    ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu",
+                          count, k, minBufferSize);
+                    break;
+                }
+                mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front();
+                mRuntimeSensorEventQueue.pop();
+                k++;
+            }
+            if (k) {
+                // record the last synthesized values
+                recordLastValueLocked(&mSensorEventBuffer[count], k);
+                count += k;
+                bufferNeedsSorting = true;
+            }
+        }
+
+        if (bufferNeedsSorting) {
+            // sort the buffer by time-stamps
+            sortEventBuffer(mSensorEventBuffer, count);
+        }
+
         // handle backward compatibility for RotationVector sensor
         if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) {
             for (int i = 0; i < count; i++) {
@@ -1342,19 +1450,37 @@
     return accessibleSensorList;
 }
 
+void SensorService::addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+        Vector<Sensor>& accessibleSensorList) {
+    if (canAccessSensor(sensor, "can't see", opPackageName)) {
+        accessibleSensorList.add(sensor);
+    } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
+        ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
+        sensor.getName().string(), sensor.getRequiredPermission().string(),
+        sensor.getRequiredAppOp());
+    }
+}
+
 Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) {
     Vector<Sensor> accessibleSensorList;
     mSensors.forEachSensor(
             [this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
                 if (sensor.isDynamicSensor()) {
-                    if (canAccessSensor(sensor, "can't see", opPackageName)) {
-                        accessibleSensorList.add(sensor);
-                    } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
-                        ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
-                              sensor.getName().string(),
-                              sensor.getRequiredPermission().string(),
-                              sensor.getRequiredAppOp());
-                    }
+                    addSensorIfAccessible(opPackageName, sensor, accessibleSensorList);
+                }
+                return true;
+            });
+    makeUuidsIntoIdsForSensorList(accessibleSensorList);
+    return accessibleSensorList;
+}
+
+Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName, int deviceId) {
+    Vector<Sensor> accessibleSensorList;
+    mSensors.forEachEntry(
+            [this, &opPackageName, deviceId, &accessibleSensorList] (
+                    const SensorServiceUtil::SensorList::Entry& e) -> bool {
+                if (e.deviceId == deviceId) {
+                    addSensorIfAccessible(opPackageName, e.si->getSensor(), accessibleSensorList);
                 }
                 return true;
             });
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 4ba3c51..e490398 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -42,6 +42,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <queue>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -143,6 +144,14 @@
         virtual void onProximityActive(bool isActive) = 0;
     };
 
+    class RuntimeSensorStateChangeCallback : public virtual RefBase {
+    public:
+        // Note that the callback is invoked from an async thread and can interact with the
+        // SensorService directly.
+        virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos,
+                                    int64_t batchReportLatencyNanos) = 0;
+    };
+
     static char const* getServiceName() ANDROID_API { return "sensorservice"; }
     SensorService() ANDROID_API;
 
@@ -169,6 +178,11 @@
     status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
     status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
 
+    int registerRuntimeSensor(const sensor_t& sensor, int deviceId,
+                              sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API;
+    status_t unregisterRuntimeSensor(int handle) ANDROID_API;
+    status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
+
     // Returns true if a sensor should be throttled according to our rate-throttling rules.
     static bool isSensorInCappedSet(int sensorType);
 
@@ -346,6 +360,7 @@
     // ISensorServer interface
     virtual Vector<Sensor> getSensorList(const String16& opPackageName);
     virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName);
+    virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId);
     virtual sp<ISensorEventConnection> createSensorEventConnection(
             const String8& packageName,
             int requestedMode, const String16& opPackageName, const String16& attributionTag);
@@ -364,8 +379,9 @@
     bool isWakeUpSensor(int type) const;
     void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
     static void sortEventBuffer(sensors_event_t* buffer, size_t count);
-    const Sensor& registerSensor(SensorInterface* sensor,
-                                 bool isDebug = false, bool isVirtual = false);
+    const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false,
+                                 bool isVirtual = false,
+                                 int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
     const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
     const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false);
     bool unregisterDynamicSensorLocked(int handle);
@@ -375,6 +391,8 @@
             sensors_event_t const* buffer, const int count);
     bool canAccessSensor(const Sensor& sensor, const char* operation,
             const String16& opPackageName);
+    void addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+            Vector<Sensor>& accessibleSensorList);
     static bool hasPermissionForSensor(const Sensor& sensor);
     static int getTargetSdkVersion(const String16& opPackageName);
     static void resetTargetSdkVersionCache(const String16& opPackageName);
@@ -492,6 +510,7 @@
     wp<const SensorEventConnection> * mMapFlushEventsToConnections;
     std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
     Mode mCurrentOperatingMode;
+    std::queue<sensors_event_t> mRuntimeSensorEventQueue;
 
     // true if the head tracker sensor type is currently restricted to system usage only
     // (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
index bbf49da..34d1de7 100644
--- a/services/sensorservice/aidl/Android.bp
+++ b/services/sensorservice/aidl/Android.bp
@@ -7,7 +7,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libsensorserviceaidl",
     srcs: [
         "EventQueue.cpp",
@@ -15,6 +15,7 @@
         "SensorManager.cpp",
         "utils.cpp",
     ],
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp
index d4e8906..c394709 100644
--- a/services/sensorservice/aidl/EventQueue.cpp
+++ b/services/sensorservice/aidl/EventQueue.cpp
@@ -34,7 +34,7 @@
                              std::shared_ptr<IEventQueueCallback> callback)
           : mQueue(queue), mCallback(callback) {}
 
-    int handleEvent(__unused int fd, __unused int events, __unused void* data) {
+    int handleEvent(int /* fd */, int /* events */, void* /* data */) {
         ASensorEvent event;
         ssize_t actual;
 
@@ -66,7 +66,6 @@
                    new EventQueueLooperCallback(internalQueue, callback), nullptr);
 }
 
-// FIXME why was this on onLastStrongRef instead of dtor?
 EventQueue::~EventQueue() {
     mLooper->removeFd(mInternalQueue->getFd());
 }
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 6d8d574..9b03344 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -201,7 +201,7 @@
         // if thread not initialized, start thread
         mStopThread = false;
         std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
-            struct sched_param p = {0};
+            struct sched_param p = {};
             p.sched_priority = 10;
             if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
                 LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
new file mode 100644
index 0000000..0d6e476
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -0,0 +1,52 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+    name: "libsensorserviceaidl_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+    ],
+    host_supported: true,
+    static_libs: [
+        "libsensorserviceaidl",
+        "libpermission",
+        "android.frameworks.sensorservice-V1-ndk",
+        "android.hardware.sensors-V1-convert",
+        "android.hardware.sensors-V1-ndk",
+        "android.hardware.common-V2-ndk",
+        "libsensor",
+        "libfakeservicemanager",
+        "libcutils",
+        "liblog",
+    ],
+    srcs: [
+        "fuzzer.cpp",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-sensors@google.com",
+            "devinmoore@google.com",
+        ],
+    },
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        diag: {
+            misc_undefined: [
+                "signed-integer-overflow",
+                "unsigned-integer-overflow",
+            ],
+        },
+        address: true,
+        integer_overflow: true,
+    },
+
+}
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
new file mode 100644
index 0000000..1b63d76
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <ServiceManager.h>
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <sensorserviceaidl/SensorManagerAidl.h>
+
+using android::fuzzService;
+using android::frameworks::sensorservice::implementation::SensorManagerAidl;
+using ndk::SharedRefBase;
+
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+    std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
+    fakeServiceManager->clear();
+
+    FuzzedDataProvider fdp(data, size);
+    android::sp<android::IBinder> binder = android::getRandomBinder(&fdp);
+    if (binder == nullptr) {
+        // Nothing to do if we get a null binder. It will cause SensorManager to
+        // hang while trying to get sensorservice.
+        return 0;
+    }
+
+    CHECK(android::NO_ERROR == fakeServiceManager->addService(android::String16("sensorservice"),
+                                   binder));
+
+    std::shared_ptr<SensorManagerAidl> sensorService =
+            ndk::SharedRefBase::make<SensorManagerAidl>(nullptr);
+
+    fuzzService(sensorService->asBinder().get(), std::move(fdp));
+
+    return 0;
+}
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
index 26bcdc5..beb38b9 100644
--- a/services/sensorservice/aidl/utils.cpp
+++ b/services/sensorservice/aidl/utils.cpp
@@ -58,7 +58,7 @@
 ::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
     ::aidl::android::hardware::sensors::Event dst;
     ::android::hardware::sensors::implementation::
-            convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+            convertFromASensorEvent(src, &dst);
     return dst;
 }
 
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
 
 ::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
     ::android::hardware::sensors::V1_0::Event dst;
-    ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
-            reinterpret_cast<const sensors_event_t&>(src), &dst);
+    ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+            src, &dst);
     return dst;
 }
 
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b1bd705..9f7a687 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -157,6 +157,7 @@
         "EventLog/EventLog.cpp",
         "FrontEnd/LayerCreationArgs.cpp",
         "FrontEnd/LayerHandle.cpp",
+        "FrontEnd/LayerHierarchy.cpp",
         "FrontEnd/LayerLifecycleManager.cpp",
         "FrontEnd/RequestedLayerState.cpp",
         "FrontEnd/TransactionHandler.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e220541..c555b39 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,7 +34,7 @@
     MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
                 (override));
     MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
index ac25fe0..d07cdf5 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -18,19 +18,19 @@
 
 #include <ftl/non_null.h>
 
-#include "DisplayHardware/DisplayMode.h"
+#include <scheduler/FrameRateMode.h>
 
 namespace android::display {
 
 struct DisplayModeRequest {
-    ftl::NonNull<DisplayModePtr> modePtr;
+    scheduler::FrameRateMode mode;
 
     // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
     bool emitEvent = false;
 };
 
 inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
-    return lhs.modePtr == rhs.modePtr && lhs.emitEvent == rhs.emitEvent;
+    return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent;
 }
 
 } // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a5195f4..46b857b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -68,6 +68,7 @@
         mCompositionDisplay{args.compositionDisplay},
         mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
+        mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
         mIsPrimary(args.isPrimary),
         mRefreshRateSelector(std::move(args.refreshRateSelector)) {
@@ -104,7 +105,9 @@
 
     mCompositionDisplay->getRenderSurface()->initialize();
 
-    if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
+    if (const auto powerModeOpt = args.initialPowerMode) {
+        setPowerMode(*powerModeOpt);
+    }
 
     // initialize the display orientation transform.
     setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -178,8 +181,7 @@
 
     mPowerMode = mode;
 
-    getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
-                                                   *mPowerMode != hal::PowerMode::OFF);
+    getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
 }
 
 void DisplayDevice::enableLayerCaching(bool enable) {
@@ -194,35 +196,32 @@
     return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
-void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
-    const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
-            [](const DisplayModePtr& mode) { return mode->getFps(); });
+void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue());
+    ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
 
-    LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode");
-    const Fps fps = *fpsOpt;
-
-    ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
-
-    mRefreshRateSelector->setActiveModeId(modeId);
+    mRefreshRateSelector->setActiveMode(modeId, renderFps);
 
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(fps);
+        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
     }
 }
 
 status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
                                            const hal::VsyncPeriodChangeConstraints& constraints,
                                            hal::VsyncPeriodChangeTimeline* outTimeline) {
-    if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) {
+    if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) {
         ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
-              info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null",
+              info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str()
+                           : "null",
               to_string(getId()).c_str());
         return BAD_VALUE;
     }
     mUpcomingActiveMode = info;
-    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
-    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
-                                                    constraints, outTimeline);
+    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
+    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
+                                                    info.modeOpt->modePtr->getHwcId(), constraints,
+                                                    outTimeline);
 }
 
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -237,7 +236,7 @@
         return vsyncPeriod;
     }
 
-    return refreshRateSelector().getActiveModePtr()->getVsyncPeriod();
+    return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -411,26 +410,35 @@
                            capabilities.getDesiredMinLuminance());
 }
 
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
     if (!enable) {
         mRefreshRateOverlay.reset();
         return;
     }
 
+    ftl::Flags<RefreshRateOverlay::Features> features;
+    if (showSpinner) {
+        features |= RefreshRateOverlay::Features::Spinner;
+    }
+
+    if (showRenderRate) {
+        features |= RefreshRateOverlay::Features::RenderRate;
+    }
+
     const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
-    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps());
+    mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
                                          bool timerExpired) {
     if (mRefreshRateSelector && mRefreshRateOverlay) {
-        const auto newRefreshRate =
+        const auto newMode =
                 mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
-        if (newRefreshRate) {
-            mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+        if (newMode) {
+            mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
             return true;
         }
     }
@@ -444,13 +452,14 @@
     }
 }
 
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction {
     ATRACE_CALL();
 
-    LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
-    LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch");
+    LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
+    LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(),
+                        "DisplayId mismatch");
 
-    ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str());
+    ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str());
 
     std::scoped_lock lock(mActiveModeLock);
     if (mDesiredActiveModeChanged) {
@@ -458,18 +467,25 @@
         const auto prevConfig = mDesiredActiveMode.event;
         mDesiredActiveMode = info;
         mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
-        return false;
+        return DesiredActiveModeAction::None;
     }
 
+    const auto& desiredMode = *info.modeOpt->modePtr;
+
     // Check if we are already at the desired mode
-    if (refreshRateSelector().getActiveModePtr()->getId() == info.mode->getId()) {
-        return false;
+    if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
+        if (refreshRateSelector().getActiveMode() == info.modeOpt) {
+            return DesiredActiveModeAction::None;
+        }
+
+        setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps);
+        return DesiredActiveModeAction::InitiateRenderRateSwitch;
     }
 
     // Initiate a mode change.
     mDesiredActiveModeChanged = true;
     mDesiredActiveMode = info;
-    return true;
+    return DesiredActiveModeAction::InitiateDisplayModeSwitch;
 }
 
 std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4b64aab..8f9b2a1 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -190,33 +190,38 @@
         using Event = scheduler::DisplayModeEvent;
 
         ActiveModeInfo() = default;
-        ActiveModeInfo(DisplayModePtr mode, Event event) : mode(std::move(mode)), event(event) {}
+        ActiveModeInfo(scheduler::FrameRateMode mode, Event event)
+              : modeOpt(std::move(mode)), event(event) {}
 
         explicit ActiveModeInfo(display::DisplayModeRequest&& request)
-              : ActiveModeInfo(std::move(request.modePtr).take(),
+              : ActiveModeInfo(std::move(request.mode),
                                request.emitEvent ? Event::Changed : Event::None) {}
 
-        DisplayModePtr mode;
+        ftl::Optional<scheduler::FrameRateMode> modeOpt;
         Event event = Event::None;
 
         bool operator!=(const ActiveModeInfo& other) const {
-            return mode != other.mode || event != other.event;
+            return modeOpt != other.modeOpt || event != other.event;
         }
     };
 
-    bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+    enum class DesiredActiveModeAction {
+        None,
+        InitiateDisplayModeSwitch,
+        InitiateRenderRateSwitch
+    };
+    DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
     std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
     void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
     ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
         return mUpcomingActiveMode;
     }
 
-    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
+    scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
         return mRefreshRateSelector->getActiveMode();
     }
 
-    // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
-    void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
+    void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps);
 
     status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
@@ -231,7 +236,8 @@
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+    void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+            REQUIRES(kMainThreadContext);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
@@ -254,6 +260,7 @@
     std::string mDisplayName;
     std::string mActiveModeFPSTrace;
     std::string mActiveModeFPSHwcTrace;
+    std::string mRenderFrameRateFPSTrace;
 
     const ui::Rotation mPhysicalOrientation;
     ui::Rotation mOrientation = ui::ROTATION_0;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3782c6c..800e36d 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -535,7 +535,7 @@
         return static_cast<Error>(status.getServiceSpecificError());
     }
 
-    *outTypes = translate<Hdr>(capabilities.types);
+    *outTypes = capabilities.types;
     *outMaxLuminance = capabilities.maxLuminance;
     *outMaxAverageLuminance = capabilities.maxAverageLuminance;
     *outMinLuminance = capabilities.minLuminance;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d84efe7..2a043fd 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -104,7 +104,7 @@
 
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
     Error getOverlaySupport(OverlayProperties* outProperties) override;
 
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index a9bf282..ec23935 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -66,7 +66,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -84,6 +83,7 @@
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
 using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
 
 class Composer {
 public:
@@ -140,7 +140,7 @@
 
     virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
     virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
-    virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+    virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                      float* outMaxLuminance, float* outMaxAverageLuminance,
                                      float* outMinLuminance) = 0;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e264570..d0126d0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -320,17 +320,17 @@
     float maxLuminance = -1.0f;
     float maxAverageLuminance = -1.0f;
     float minLuminance = -1.0f;
-    std::vector<Hwc2::Hdr> types;
-    auto intError = mComposer.getHdrCapabilities(mId, &types,
-            &maxLuminance, &maxAverageLuminance, &minLuminance);
+    std::vector<Hwc2::Hdr> hdrTypes;
+    auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+                                                 &maxAverageLuminance, &minLuminance);
     auto error = static_cast<HWC2::Error>(intError);
 
     if (error != Error::NONE) {
         return error;
     }
 
-    *outCapabilities = HdrCapabilities(std::move(types),
-            maxLuminance, maxAverageLuminance, minLuminance);
+    *outCapabilities =
+            HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
     return Error::NONE;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 8238828..537d545 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
+#include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
@@ -39,7 +40,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Error;
@@ -69,6 +69,7 @@
 using PowerMode = IComposerClient::PowerMode;
 using Vsync = IComposerClient::Vsync;
 using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
 
 } // namespace hardware::graphics::composer::hal
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index f8522e2..b607df0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -496,13 +496,13 @@
                      "OptionalFeature::KernelIdleTimer is not supported on HIDL");
 }
 
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                        float* outMaxLuminance, float* outMaxAverageLuminance,
                                        float* outMinLuminance) {
     Error error = kDefaultError;
     if (mClient_2_3) {
         mClient_2_3->getHdrCapabilities_2_3(display,
-                                            [&](const auto& tmpError, const auto& tmpTypes,
+                                            [&](const auto& tmpError, const auto& tmpHdrTypes,
                                                 const auto& tmpMaxLuminance,
                                                 const auto& tmpMaxAverageLuminance,
                                                 const auto& tmpMinLuminance) {
@@ -510,15 +510,15 @@
                                                 if (error != Error::NONE) {
                                                     return;
                                                 }
+                                                *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
 
-                                                *outTypes = tmpTypes;
                                                 *outMaxLuminance = tmpMaxLuminance;
                                                 *outMaxAverageLuminance = tmpMaxAverageLuminance;
                                                 *outMinLuminance = tmpMinLuminance;
                                             });
     } else {
         mClient->getHdrCapabilities(display,
-                                    [&](const auto& tmpError, const auto& tmpTypes,
+                                    [&](const auto& tmpError, const auto& tmpHdrTypes,
                                         const auto& tmpMaxLuminance,
                                         const auto& tmpMaxAverageLuminance,
                                         const auto& tmpMinLuminance) {
@@ -526,11 +526,7 @@
                                         if (error != Error::NONE) {
                                             return;
                                         }
-
-                                        outTypes->clear();
-                                        for (auto type : tmpTypes) {
-                                            outTypes->push_back(static_cast<Hdr>(type));
-                                        }
+                                        *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
 
                                         *outMaxLuminance = tmpMaxLuminance;
                                         *outMaxAverageLuminance = tmpMaxAverageLuminance;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 48b720c..3602bbb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -209,7 +208,7 @@
 
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
     Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
                                     outProperties) override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index cb2c8c5..f05223c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -57,6 +57,7 @@
 using android::hardware::power::IPower;
 using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
+using android::hardware::power::SessionHint;
 using android::hardware::power::WorkDuration;
 
 PowerAdvisor::~PowerAdvisor() = default;
@@ -140,7 +141,7 @@
     }
 }
 
-void PowerAdvisor::notifyDisplayUpdateImminent() {
+void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
     // Only start sending this notification once the system has booted so we don't introduce an
     // early-boot dependency on Power HAL
     if (!mBootFinished.load()) {
@@ -154,7 +155,7 @@
             return;
         }
 
-        if (!halWrapper->notifyDisplayUpdateImminent()) {
+        if (!halWrapper->notifyDisplayUpdateImminentAndCpuReset()) {
             // The HAL has become unavailable; attempt to reconnect later
             mReconnectPowerHal = true;
             return;
@@ -599,7 +600,7 @@
         return ret.isOk();
     }
 
-    bool notifyDisplayUpdateImminent() override {
+    bool notifyDisplayUpdateImminentAndCpuReset() override {
         // Power HAL 1.x doesn't have a notification for this
         ALOGV("HIDL notifyUpdateImminent received but can't send");
         return true;
@@ -675,8 +676,12 @@
     return ret.isOk();
 }
 
-bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
-    ALOGV("AIDL notifyDisplayUpdateImminent");
+bool AidlPowerHalWrapper::notifyDisplayUpdateImminentAndCpuReset() {
+    ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
+    if (isPowerHintSessionRunning()) {
+        mPowerHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+    }
+
     if (!mHasDisplayUpdateImminent) {
         ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
         return true;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 1c9d123..d45e7cb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -48,7 +48,7 @@
     virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual bool isUsingExpensiveRendering() = 0;
-    virtual void notifyDisplayUpdateImminent() = 0;
+    virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
     // Checks both if it supports and if it's enabled
     virtual bool usePowerHintSession() = 0;
     virtual bool supportsPowerHintSession() = 0;
@@ -106,7 +106,7 @@
         virtual ~HalWrapper() = default;
 
         virtual bool setExpensiveRendering(bool enabled) = 0;
-        virtual bool notifyDisplayUpdateImminent() = 0;
+        virtual bool notifyDisplayUpdateImminentAndCpuReset() = 0;
         virtual bool supportsPowerHintSession() = 0;
         virtual bool isPowerHintSessionRunning() = 0;
         virtual void restartPowerHintSession() = 0;
@@ -126,7 +126,7 @@
     void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
     bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
-    void notifyDisplayUpdateImminent() override;
+    void notifyDisplayUpdateImminentAndCpuReset() override;
     bool usePowerHintSession() override;
     bool supportsPowerHintSession() override;
     bool isPowerHintSessionRunning() override;
@@ -289,7 +289,7 @@
     static std::unique_ptr<HalWrapper> connect();
 
     bool setExpensiveRendering(bool enabled) override;
-    bool notifyDisplayUpdateImminent() override;
+    bool notifyDisplayUpdateImminentAndCpuReset() override;
     bool supportsPowerHintSession() override;
     bool isPowerHintSessionRunning() override;
     void restartPowerHintSession() override;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..db4e8af
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2022 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+                        const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+    auto lhsLayer = lhs.first->getLayer();
+    auto rhsLayer = rhs.first->getLayer();
+    if (lhsLayer->layerStack != rhsLayer->layerStack) {
+        return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+    }
+    if (lhsLayer->z != rhsLayer->z) {
+        return lhsLayer->z < rhsLayer->z;
+    }
+    return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+                  std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+    auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+    vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+    mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+    mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+                              LayerHierarchy::TraversalPath& traversalPath) const {
+    if (mLayer) {
+        bool breakTraversal = !visitor(*this, traversalPath);
+        if (breakTraversal) {
+            return;
+        }
+    }
+    if (traversalPath.hasRelZLoop()) {
+        LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+    }
+    for (auto& [child, childVariant] : mChildren) {
+        ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+                                                         childVariant);
+        child->traverse(visitor, traversalPath);
+    }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+                                      LayerHierarchy::TraversalPath& traversalPath) const {
+    bool traverseThisLayer = (mLayer != nullptr);
+    for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+        auto& [child, childVariant] = *it;
+        if (traverseThisLayer && child->getLayer()->z >= 0) {
+            bool breakTraversal = !visitor(*this, traversalPath);
+            if (breakTraversal) {
+                return;
+            }
+            traverseThisLayer = false;
+        }
+        if (childVariant == LayerHierarchy::Variant::Detached) {
+            continue;
+        }
+        ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+                                                         childVariant);
+        child->traverseInZOrder(visitor, traversalPath);
+    }
+
+    if (traverseThisLayer) {
+        visitor(*this, traversalPath);
+    }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+    insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+    auto it = std::find_if(mChildren.begin(), mChildren.end(),
+                           [child](const std::pair<LayerHierarchy*, Variant>& x) {
+                               return x.first == child;
+                           });
+    if (it == mChildren.end()) {
+        LOG_ALWAYS_FATAL("Could not find child!");
+    }
+    mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+    std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+    auto it = std::find_if(mChildren.begin(), mChildren.end(),
+                           [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+                               return child.first == hierarchy;
+                           });
+    if (it == mChildren.end()) {
+        LOG_ALWAYS_FATAL("Could not find child!");
+    } else {
+        it->second = variant;
+    }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+    return mLayer;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+    std::string debug = "LayerHierarchy{";
+    debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " ";
+    if (mChildren.empty()) {
+        debug += "no children";
+    } else {
+        debug += std::to_string(mChildren.size()) + " children";
+    }
+    return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+    std::string debug = prefix + getDebugStringShort();
+    for (auto& [child, childVariant] : mChildren) {
+        std::string childPrefix = "  " + std::string(prefix) + " " + std::to_string(childVariant);
+        debug += "\n" + child->getDebugString(childPrefix.c_str());
+    }
+    return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+    outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+    traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+                                       const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+        if (traversalPath.hasRelZLoop()) {
+            outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+            return false;
+        }
+        return true;
+    });
+    return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+        const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+    mHierarchies.reserve(layers.size());
+    mLayerIdToHierarchy.reserve(layers.size());
+    for (auto& layer : layers) {
+        mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+        mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+    }
+    for (const auto& layer : layers) {
+        onLayerAdded(layer.get());
+    }
+    detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+    auto layer = hierarchy->mLayer;
+    LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+            ? LayerHierarchy::Variant::Detached
+            : LayerHierarchy::Variant::Attached;
+
+    LayerHierarchy* parent;
+
+    if (layer->parentId != UNASSIGNED_LAYER_ID) {
+        parent = getHierarchyFromId(layer->parentId);
+    } else if (layer->canBeRoot) {
+        parent = &mRoot;
+    } else {
+        parent = &mOffscreenRoot;
+    }
+    parent->addChild(hierarchy, type);
+    hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+    hierarchy->mParent->removeChild(hierarchy);
+    hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+    auto layer = hierarchy->mLayer;
+    if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+        return;
+    }
+
+    if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+        hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+    } else {
+        hierarchy->mRelativeParent = &mOffscreenRoot;
+    }
+    hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+    hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+    if (hierarchy->mRelativeParent) {
+        hierarchy->mRelativeParent->removeChild(hierarchy);
+    }
+    hierarchy->mRelativeParent = nullptr;
+    hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+    if (root->mLayer) {
+        attachToRelativeParent(root);
+    }
+    for (auto& [child, childVariant] : root->mChildren) {
+        if (childVariant == LayerHierarchy::Variant::Detached ||
+            childVariant == LayerHierarchy::Variant::Attached) {
+            attachHierarchyToRelativeParent(child);
+        }
+    }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+    if (root->mLayer) {
+        detachFromRelativeParent(root);
+    }
+    for (auto& [child, childVariant] : root->mChildren) {
+        if (childVariant == LayerHierarchy::Variant::Detached ||
+            childVariant == LayerHierarchy::Variant::Attached) {
+            detachHierarchyFromRelativeParent(child);
+        }
+    }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+    attachToParent(hierarchy);
+    attachToRelativeParent(hierarchy);
+
+    if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+        LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+        hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+    }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+    if (!hierarchy) {
+        // Layer was never part of the hierarchy if it was created and destroyed in the same
+        // transaction.
+        return;
+    }
+    // detach from parent
+    detachFromRelativeParent(hierarchy);
+    detachFromParent(hierarchy);
+
+    // detach children
+    for (auto& [child, variant] : hierarchy->mChildren) {
+        if (variant == LayerHierarchy::Variant::Attached ||
+            variant == LayerHierarchy::Variant::Detached) {
+            mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+            child->mParent = &mOffscreenRoot;
+        } else if (variant == LayerHierarchy::Variant::Relative) {
+            mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+            child->mRelativeParent = &mOffscreenRoot;
+        }
+    }
+
+    swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+        return layerHierarchy.get() == hierarchy;
+    });
+    mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+    auto it = hierarchy->mChildren.begin();
+    while (it != hierarchy->mChildren.end()) {
+        if (it->second == LayerHierarchy::Variant::Mirror) {
+            hierarchy->mChildren.erase(it);
+            break;
+        }
+        it++;
+    }
+
+    if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+        hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+    }
+}
+
+void LayerHierarchyBuilder::update(
+        const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+        const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+    // rebuild map
+    for (auto& layer : layers) {
+        if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+            mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+            mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+        }
+    }
+
+    for (auto& layer : layers) {
+        if (layer->changes.get() == 0) {
+            continue;
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+            onLayerAdded(layer.get());
+            continue;
+        }
+        LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+        if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+            detachFromParent(hierarchy);
+            attachToParent(hierarchy);
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+            detachFromRelativeParent(hierarchy);
+            attachToRelativeParent(hierarchy);
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+            hierarchy->mParent->sortChildrenByZOrder();
+            if (hierarchy->mRelativeParent) {
+                hierarchy->mRelativeParent->sortChildrenByZOrder();
+            }
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+            updateMirrorLayer(layer.get());
+        }
+    }
+
+    for (auto& layer : destroyedLayers) {
+        onLayerDestroyed(layer.get());
+    }
+    // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+    // from our relative parents. This walks down both trees to do so. We can optimize this
+    // further by tracking onscreen, offscreen state in LayerHierarchy.
+    detachHierarchyFromRelativeParent(&mOffscreenRoot);
+    attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+    return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+    return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+    if (depth > 10) return "too deep, loop?";
+    if (layerId == UNASSIGNED_LAYER_ID) return "";
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) return "not found";
+
+    LayerHierarchy* hierarchy = it->second;
+    if (!hierarchy->mLayer) return "none";
+
+    std::string debug =
+            "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+    if (hierarchy->mRelativeParent) {
+        debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+    }
+    if (hierarchy->mParent) {
+        debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+    }
+    return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+                                                          bool childrenOnly) const {
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+    LayerHierarchy hierarchy(*it->second, childrenOnly);
+    return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) {
+        if (crashOnFailure) {
+            LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+        }
+        return nullptr;
+    };
+
+    return it->second;
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID =
+        {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+    std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+
+    if (!mirrorRootIds.empty()) {
+        debugString += ", .mirrorRootIds=";
+        for (auto rootId : mirrorRootIds) {
+            debugString += std::to_string(rootId) + ",";
+        }
+    }
+
+    if (!relativeRootIds.empty()) {
+        debugString += ", .relativeRootIds=";
+        for (auto rootId : relativeRootIds) {
+            debugString += std::to_string(rootId) + ",";
+        }
+    }
+
+    if (hasRelZLoop()) {
+        debugString += ", hasRelZLoop=true invalidRelativeRootId=";
+        debugString += std::to_string(invalidRelativeRootId) + ",";
+    }
+
+    debugString += "}";
+    return debugString;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+                                                                   uint32_t layerId,
+                                                                   LayerHierarchy::Variant variant)
+      : mTraversalPath(traversalPath),
+        mParentId(traversalPath.id),
+        mParentVariant(traversalPath.variant) {
+    // Update the traversal id with the child layer id and variant. Parent id and variant are
+    // stored to reset the id upon destruction.
+    traversalPath.id = layerId;
+    traversalPath.variant = variant;
+    if (variant == LayerHierarchy::Variant::Mirror) {
+        traversalPath.mirrorRootIds.emplace_back(layerId);
+    }
+    if (variant == LayerHierarchy::Variant::Relative) {
+        if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+                      layerId) != traversalPath.relativeRootIds.end()) {
+            traversalPath.invalidRelativeRootId = layerId;
+        }
+        traversalPath.relativeRootIds.emplace_back(layerId);
+    }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+    // Reset the traversal id to its original parent state using the state that was saved in
+    // the constructor.
+    if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+        mTraversalPath.mirrorRootIds.pop_back();
+    }
+    if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+        mTraversalPath.relativeRootIds.pop_back();
+    }
+    if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+        mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+    }
+    mTraversalPath.id = mParentId;
+    mTraversalPath.variant = mParentVariant;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..f83a859
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 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 "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+    enum Variant {
+        Attached,
+        Detached,
+        Relative,
+        Mirror,
+    };
+    // Represents a unique path to a node.
+    struct TraversalPath {
+        uint32_t id;
+        LayerHierarchy::Variant variant;
+        // Mirrored layers can have a different geometry than their parents so we need to track
+        // the mirror roots in the traversal.
+        ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+        // Relative layers can be visited twice, once by their parent and then once again by
+        // their relative parent. We keep track of the roots here to detect any loops in the
+        // hierarchy. If a relative root already exists in the list while building the
+        // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+        // parented to each other.
+        ftl::SmallVector<uint32_t, 5> relativeRootIds;
+        // First duplicate relative root id found. If this is a valid layer id that means we are
+        // in a loop.
+        uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+        bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+        bool isRelative() { return !relativeRootIds.empty(); }
+
+        bool operator==(const TraversalPath& other) const {
+            return id == other.id && mirrorRootIds == other.mirrorRootIds;
+        }
+        std::string toString() const;
+
+        static TraversalPath ROOT_TRAVERSAL_ID;
+    };
+
+    // Helper class to add nodes to an existing traversal id and removes the
+    // node when it goes out of scope.
+    class ScopedAddToTraversalPath {
+    public:
+        ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+                                 LayerHierarchy::Variant variantArg);
+        ~ScopedAddToTraversalPath();
+
+    private:
+        TraversalPath& mTraversalPath;
+        uint32_t mParentId;
+        LayerHierarchy::Variant mParentVariant;
+    };
+    LayerHierarchy(RequestedLayerState* layer);
+
+    // Visitor function that provides the hierarchy node and a traversal id which uniquely
+    // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+    // Return false to stop traversing down the hierarchy.
+    typedef std::function<bool(const LayerHierarchy& hierarchy,
+                               const LayerHierarchy::TraversalPath& traversalPath)>
+            Visitor;
+
+    // Traverse the hierarchy and visit all child variants.
+    void traverse(const Visitor& visitor) const {
+        traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+    }
+
+    // Traverse the hierarchy in z-order, skipping children that have relative parents.
+    void traverseInZOrder(const Visitor& visitor) const {
+        traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+    }
+
+    const RequestedLayerState* getLayer() const;
+    std::string getDebugString(const char* prefix = "") const;
+    std::string getDebugStringShort() const;
+    // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+    // will contain the first relative root that was visited twice in a traversal.
+    bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+    std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+    friend LayerHierarchyBuilder;
+    LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+    void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+    void removeChild(LayerHierarchy*);
+    void sortChildrenByZOrder();
+    void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+    void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+    void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+    const RequestedLayerState* mLayer;
+    LayerHierarchy* mParent = nullptr;
+    LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// hierarchy from a list of RequestedLayerState and associated change flags.
+class LayerHierarchyBuilder {
+public:
+    LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+    void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+                const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+    LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+    const LayerHierarchy& getHierarchy() const;
+    const LayerHierarchy& getOffscreenHierarchy() const;
+    std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+    void onLayerAdded(RequestedLayerState* layer);
+    void attachToParent(LayerHierarchy*);
+    void detachFromParent(LayerHierarchy*);
+    void attachToRelativeParent(LayerHierarchy*);
+    void detachFromRelativeParent(LayerHierarchy*);
+    void attachHierarchyToRelativeParent(LayerHierarchy*);
+    void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+    void onLayerDestroyed(RequestedLayerState* layer);
+    void updateMirrorLayer(RequestedLayerState* layer);
+    LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+    std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
+    std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
+    LayerHierarchy mRoot{nullptr};
+    LayerHierarchy mOffscreenRoot{nullptr};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 1108246..fdf60b3 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -42,10 +42,10 @@
                              it->second.owner.getDebugString().c_str());
         }
 
-        linkLayer(layer.parentId, layer.id);
-        linkLayer(layer.relativeParentId, layer.id);
-        linkLayer(layer.mirrorId, layer.id);
-        linkLayer(layer.touchCropId, layer.id);
+        layer.parentId = linkLayer(layer.parentId, layer.id);
+        layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+        layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+        layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
 
         mLayers.emplace_back(std::move(newLayer));
     }
@@ -83,10 +83,10 @@
 
         RequestedLayerState& layer = it->second.owner;
 
-        unlinkLayer(layer.parentId, layer.id);
-        unlinkLayer(layer.relativeParentId, layer.id);
-        unlinkLayer(layer.mirrorId, layer.id);
-        unlinkLayer(layer.touchCropId, layer.id);
+        layer.parentId = unlinkLayer(layer.parentId, layer.id);
+        layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+        layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+        layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
 
         auto& references = it->second.references;
         for (uint32_t linkedLayerId : references) {
@@ -123,7 +123,11 @@
             ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str());
             std::iter_swap(it, mLayers.end() - 1);
             mDestroyedLayers.emplace_back(std::move(mLayers.back()));
-            mLayers.erase(mLayers.end() - 1);
+            if (it == mLayers.end() - 1) {
+                it = mLayers.erase(mLayers.end() - 1);
+            } else {
+                mLayers.erase(mLayers.end() - 1);
+            }
         } else {
             it++;
         }
@@ -195,15 +199,15 @@
 
             if (oldParentId != layer->parentId) {
                 unlinkLayer(oldParentId, layer->id);
-                linkLayer(layer->parentId, layer->id);
+                layer->parentId = linkLayer(layer->parentId, layer->id);
             }
             if (oldRelativeParentId != layer->relativeParentId) {
                 unlinkLayer(oldRelativeParentId, layer->id);
-                linkLayer(layer->relativeParentId, layer->id);
+                layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
             }
             if (oldTouchCropId != layer->touchCropId) {
                 unlinkLayer(oldTouchCropId, layer->id);
-                linkLayer(layer->touchCropId, layer->id);
+                layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
             }
 
             mGlobalChanges |= layer->changes &
@@ -283,26 +287,28 @@
     return &it->second.references;
 }
 
-void LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
-    if (layerToLink && layerId != UNASSIGNED_LAYER_ID) {
-        std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
-        if (!linkedLayers) {
-            LOG_ALWAYS_FATAL("Could not find layer id %d to link %d", layerId, layerToLink);
-            return;
-        }
-        linkedLayers->emplace_back(layerToLink);
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+    if (layerId == UNASSIGNED_LAYER_ID) {
+        return UNASSIGNED_LAYER_ID;
     }
-}
-
-void LayerLifecycleManager::unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer) {
-    uint32_t layerId = inOutLayerId;
-    inOutLayerId = UNASSIGNED_LAYER_ID;
 
     std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
     if (!linkedLayers) {
-        return;
+        ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+              layerToLink);
+        return UNASSIGNED_LAYER_ID;
+    }
+    linkedLayers->emplace_back(layerToLink);
+    return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+    std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+    if (!linkedLayers) {
+        return UNASSIGNED_LAYER_ID;
     }
     swapErase(*linkedLayers, linkedLayer);
+    return UNASSIGNED_LAYER_ID;
 }
 
 std::string LayerLifecycleManager::References::getDebugString() const {
@@ -314,4 +320,16 @@
     return debugInfo;
 }
 
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+    auto it = mIdToLayer.find(relativeRootId);
+    if (it == mIdToLayer.end()) {
+        return;
+    }
+    RequestedLayerState& layer = it->second.owner;
+    layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+    layer.changes |=
+            RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+    mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index ad70d3f..63a7afc 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -42,6 +42,12 @@
     void applyTransactions(const std::vector<TransactionState>&);
     void onHandlesDestroyed(const std::vector<uint32_t>&);
 
+    // Detaches the layer from its relative parent to prevent a loop in the
+    // layer hierarchy. This overrides the RequestedLayerState and leaves
+    // the system in an invalid state. This is always a client error that
+    // needs to be fixed but overriding the state allows us to fail gracefully.
+    void fixRelativeZLoop(uint32_t relativeRootId);
+
     // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
     // ILifecycleListener callbacks and clears any change flags from previous state
     // updates. This function should be called outside the hot path since it's not
@@ -72,8 +78,8 @@
 
     RequestedLayerState* getLayerFromId(uint32_t);
     std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
-    void linkLayer(uint32_t layerId, uint32_t layerToLink);
-    void unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer);
+    uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+    uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
 
     struct References {
         // Lifetime tied to mLayers
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 45058d9..e2a4ed1 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -40,7 +40,7 @@
 }
 
 std::string layerIdToString(uint32_t layerId) {
-    return layerId == UNASSIGNED_LAYER_ID ? std::to_string(layerId) : "none";
+    return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
 }
 
 } // namespace
@@ -147,13 +147,17 @@
         static const mat4 identityMatrix = mat4();
         hasColorTransform = colorTransform != identityMatrix;
     }
-    if (clientState.what & layer_state_t::eLayerChanged) {
+    if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
         changes |= RequestedLayerState::Changes::Z;
     }
     if (clientState.what & layer_state_t::eReparent) {
         changes |= RequestedLayerState::Changes::Parent;
         parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
         parentSurfaceControlForChild = nullptr;
+        // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+        // but thats the existing logic and until we make this behavior more explicit, we need
+        // to maintain this logic.
+        canBeRoot = false;
     }
     if (clientState.what & layer_state_t::eRelativeLayerChanged) {
         changes |= RequestedLayerState::Changes::RelativeParent;
@@ -254,7 +258,7 @@
             ",relativeParent=" + layerIdToString(relativeParentId) +
             ",isRelativeOf=" + std::to_string(isRelativeOf) +
             ",mirrorId=" + layerIdToString(mirrorId) +
-            ",handleAlive=" + std::to_string(handleAlive);
+            ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
 }
 
 std::string RequestedLayerState::getDebugStringShort() const {
@@ -355,4 +359,13 @@
     return Region(win).subtract(exclude).getBounds();
 }
 
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+    return isRelativeOf && parentId != relativeParentId;
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ddf5e2..6891fbc 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -62,13 +62,14 @@
     std::string getDebugString() const;
     std::string getDebugStringShort() const;
     aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+    bool hasValidRelativeParent() const;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
     // the same.
     const uint32_t id;
     const std::string name;
-    const bool canBeRoot = false;
+    bool canBeRoot = false;
     const uint32_t layerCreationFlags;
     const uint32_t textureName;
     // The owner of the layer. If created from a non system process, it will be the calling uid.
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 8629671..c2109b3 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -177,7 +177,7 @@
     }
 
     mStalledTransactions.push_back(transactionId);
-    listener->onTransactionQueueStalled(String8(reason.c_str()));
+    listener->onTransactionQueueStalled(reason);
 }
 
 void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index a06b870..475ff1b 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -29,6 +29,7 @@
 namespace android {
 
 class TestableSurfaceFlinger;
+using gui::IListenerHash;
 namespace surfaceflinger::frontend {
 
 class TransactionHandler {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f4006a6..ab90357 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2605,9 +2605,12 @@
         return;
     }
     ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
-    listener->onReleaseBuffer({buffer->getId(), framenumber},
-                              releaseFence ? releaseFence : Fence::NO_FENCE,
-                              currentMaxAcquiredBufferCount);
+    std::optional<os::ParcelFileDescriptor> fenceFd;
+    if (releaseFence) {
+        fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+    }
+    listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
+                              static_cast<int32_t>(currentMaxAcquiredBufferCount));
 }
 
 void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
@@ -3598,7 +3601,7 @@
     }
 
     if (display) {
-        const Fps refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
+        const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
 constexpr int kDigitHeight = 100;
 constexpr int kDigitSpace = 16;
 
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
 constexpr int kBufferHeight = kDigitHeight;
 
 SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
         drawSegment(Segment::Bottom, left, color, canvas);
 }
 
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
                                                   ui::Transform::RotationFlags rotation,
-                                                  bool showSpinner) -> Buffers {
-    if (number < 0 || number > 1000) return {};
-
-    const auto hundreds = number / 100;
-    const auto tens = (number / 10) % 10;
-    const auto ones = number % 10;
-
-    const size_t loopCount = showSpinner ? 6 : 1;
+                                                  ftl::Flags<Features> features) -> Buffers {
+    const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
 
     Buffers buffers;
     buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
         canvas->setMatrix(canvasTransform);
 
         int left = 0;
-        if (hundreds != 0) {
-            drawDigit(hundreds, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        if (tens != 0) {
-            drawDigit(tens, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        drawDigit(ones, left, color, *canvas);
-        left += kDigitWidth + kDigitSpace;
-
-        if (showSpinner) {
+        drawNumber(displayFps, left, color, *canvas);
+        left += 3 * (kDigitWidth + kDigitSpace);
+        if (features.test(Features::Spinner)) {
             switch (i) {
                 case 0:
                     drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
             }
         }
 
+        left += kDigitWidth + kDigitSpace;
+
+        if (features.test(Features::RenderRate)) {
+            drawNumber(renderFps, left, color, *canvas);
+        }
+        left += 3 * (kDigitWidth + kDigitSpace);
+
         void* pixels = nullptr;
         buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
 
@@ -219,6 +209,23 @@
     return buffers;
 }
 
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+                                                        SkCanvas& canvas) {
+    if (number < 0 || number >= 1000) return;
+
+    if (number >= 100) {
+        drawDigit(number / 100, left, color, canvas);
+    }
+    left += kDigitWidth + kDigitSpace;
+
+    if (number >= 10) {
+        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()
@@ -228,10 +235,8 @@
     return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
 }
 
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
-      : mFpsRange(fpsRange),
-        mShowSpinner(showSpinner),
-        mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+      : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
@@ -243,10 +248,15 @@
             .apply();
 }
 
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
     static const Buffers kNoBuffers;
     if (!mSurfaceControl) return kNoBuffers;
 
+    // avoid caching different render rates if RenderRate is anyway not visible
+    if (!mFeatures.test(Features::RenderRate)) {
+        renderFps = 0_Hz;
+    }
+
     const auto transformHint =
             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
 
@@ -266,17 +276,20 @@
             .setTransform(mSurfaceControl->get(), transform)
             .apply();
 
-    BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+    BufferCache::const_iterator it =
+            mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
         const int minFps = mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
-        // Clamp to the range. The current fps may be outside of this range if the display has
-        // changed its set of supported refresh rates.
-        const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+        // Clamp to the range. The current displayFps may be outside of this range if the display
+        // has changed its set of supported refresh rates.
+        const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+        const int renderIntFps = renderFps.getIntValue();
 
         // Ensure non-zero range to avoid division by zero.
-        const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+        const float fpsScale =
+                static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
 
         constexpr SkColor kMinFpsColor = SK_ColorRED;
         constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
-        it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+        auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+                                                mFeatures);
+        it = mBufferCache
+                     .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+                     .first;
     }
 
     return it->second;
@@ -303,7 +319,7 @@
     constexpr int32_t kMaxWidth = 1000;
     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
     const auto height = 2 * width;
-    Rect frame((3 * width) >> 4, height >> 5);
+    Rect frame((5 * width) >> 4, height >> 5);
     frame.offsetBy(width >> 5, height >> 4);
 
     createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
     createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
-    mCurrentFps = fps;
-    const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+    mDisplayFps = displayFps;
+    mRenderFps = renderFps;
+    const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
     createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
-    if (!mShowSpinner || !mCurrentFps) return;
+    if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
 
-    const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+    const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
     createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
 #include <SkColor.h>
 #include <vector>
 
+#include <ftl/flags.h>
 #include <ftl/small_map.h>
 #include <ui/LayerStack.h>
 #include <ui/Size.h>
@@ -50,11 +51,16 @@
 
 class RefreshRateOverlay {
 public:
-    RefreshRateOverlay(FpsRange, bool showSpinner);
+    enum class Features {
+        Spinner = 1 << 0,
+        RenderRate = 1 << 1,
+    };
+
+    RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
 
     void setLayerStack(ui::LayerStack);
     void setViewport(ui::Size);
-    void changeRefreshRate(Fps);
+    void changeRefreshRate(Fps, Fps);
     void animate();
 
 private:
@@ -62,32 +68,39 @@
 
     class SevenSegmentDrawer {
     public:
-        static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+        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&);
     };
 
-    const Buffers& getOrCreateBuffers(Fps);
+    const Buffers& getOrCreateBuffers(Fps, Fps);
 
     struct Key {
-        int fps;
+        int displayFps;
+        int renderFps;
         ui::Transform::RotationFlags flags;
 
-        bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+        bool operator==(Key other) const {
+            return displayFps == other.displayFps && renderFps == other.renderFps &&
+                    flags == other.flags;
+        }
     };
 
     using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
     BufferCache mBufferCache;
 
-    std::optional<Fps> mCurrentFps;
+    std::optional<Fps> mDisplayFps;
+    std::optional<Fps> mRenderFps;
     size_t mFrame = 0;
 
     const FpsRange mFpsRange; // For color interpolation.
-    const bool mShowSpinner;
+    const ftl::Flags<Features> mFeatures;
 
     const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
 };
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1acf15a..008d8c4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -124,12 +124,12 @@
     return event;
 }
 
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
     DisplayEventReceiver::Event event;
-    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
-                    systemTime()};
-    event.modeChange.modeId = mode->getId().value();
-    event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+                    mode.modePtr->getPhysicalDisplayId(), systemTime()};
+    event.modeChange.modeId = mode.modePtr->getId().value();
+    event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
     return event;
 }
 
@@ -405,7 +405,7 @@
     mCondition.notify_all();
 }
 
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     mPendingEvents.push_back(makeModeChanged(mode));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7a5a348..43c3598 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
 #include <sys/types.h>
 #include <utils/Errors.h>
 
+#include <scheduler/FrameRateMode.h>
 #include <condition_variable>
 #include <cstdint>
 #include <deque>
@@ -134,7 +135,7 @@
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     // called when SF changes the active mode and apps needs to be notified about the change
-    virtual void onModeChanged(DisplayModePtr) = 0;
+    virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
 
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -185,7 +186,7 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onModeChanged(DisplayModePtr) override;
+    void onModeChanged(const scheduler::FrameRateMode&) override;
 
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index fd1a733..a05d3df 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -107,7 +107,8 @@
     }
 
     using fps_approx_ops::operator/;
-    const auto start = std::max(1u, fps / range.max - 1);
+    // use signed type as `fps / range.max` might be 0
+    const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
     const auto end = fps /
             std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
                      fps_approx_ops::operator<);
@@ -213,7 +214,7 @@
     std::vector<FrameRateMode> frameRateModes;
     frameRateModes.reserve(ratesMap.size());
     for (const auto& [key, mode] : ratesMap) {
-        frameRateModes.emplace_back(FrameRateMode{key.fps, mode->second});
+        frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)});
     }
 
     // We always want that the lowest frame rate will be corresponding to the
@@ -409,7 +410,7 @@
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
-    const auto& activeMode = *getActiveModeItLocked()->second;
+    const auto& activeMode = *getActiveModeLocked().modePtr;
 
     // Keep the display at max frame rate for the duration of powering on the display.
     if (signals.powerOnImminent) {
@@ -836,29 +837,33 @@
     return frameRateOverrides;
 }
 
-std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
         std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
     std::lock_guard lock(mLock);
 
-    const DisplayModePtr& current = desiredActiveModeId
-            ? mDisplayModes.get(*desiredActiveModeId)->get()
-            : getActiveModeItLocked()->second;
+    const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+        if (desiredActiveModeId) {
+            const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+            return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+        }
+
+        return getActiveModeLocked();
+    }();
 
     const DisplayModePtr& min = mMinRefreshRateModeIt->second;
-    if (current == min) {
+    if (current.modePtr->getId() == min->getId()) {
         return {};
     }
 
-    const auto& mode = timerExpired ? min : current;
-    return mode->getFps();
+    return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
 }
 
 const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
-    const auto& activeMode = *getActiveModeItLocked()->second;
+    const auto& activeMode = *getActiveModeLocked().modePtr;
 
     for (const FrameRateMode& mode : mPrimaryFrameRates) {
         if (activeMode.getGroup() == mode.modePtr->getGroup()) {
-            return mode.modePtr;
+            return mode.modePtr.get();
         }
     }
 
@@ -866,12 +871,12 @@
           to_string(activeMode).c_str());
 
     // Default to the lowest refresh rate.
-    return mPrimaryFrameRates.front().modePtr;
+    return mPrimaryFrameRates.front().modePtr.get();
 }
 
 const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
-    const DisplayModePtr* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
-    const DisplayModePtr* max = &mPrimaryFrameRates.back().modePtr;
+    const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+    const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr;
 
     bool maxByAnchorFound = false;
     for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
@@ -888,13 +893,13 @@
     }
 
     if (maxByAnchorFound) {
-        return *maxByAnchor;
+        return maxByAnchor->get();
     }
 
     ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
 
     // Default to the highest refresh rate.
-    return *max;
+    return max->get();
 }
 
 auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
@@ -946,31 +951,26 @@
     return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
 }
 
-DisplayModePtr RefreshRateSelector::getActiveModePtr() const {
+FrameRateMode RefreshRateSelector::getActiveMode() const {
     std::lock_guard lock(mLock);
-    return getActiveModeItLocked()->second;
+    return getActiveModeLocked();
 }
 
-const DisplayMode& RefreshRateSelector::getActiveMode() const {
-    // Reads from kMainThreadContext do not require mLock.
-    ftl::FakeGuard guard(mLock);
-    return *mActiveModeIt->second;
+const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const {
+    return *mActiveModeOpt;
 }
 
-DisplayModeIterator RefreshRateSelector::getActiveModeItLocked() const {
-    // Reads under mLock do not require kMainThreadContext.
-    return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
-}
-
-void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) {
+void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
     std::lock_guard lock(mLock);
 
     // Invalidate the cached invocation to getRankedFrameRates. This forces
     // the refresh rate to be recomputed on the next call to getRankedFrameRates.
     mGetRankedFrameRatesCache.reset();
 
-    mActiveModeIt = mDisplayModes.find(modeId);
-    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
+    const auto activeModeOpt = mDisplayModes.get(modeId);
+    LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+
+    mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())});
 }
 
 RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
@@ -1007,8 +1007,10 @@
     mGetRankedFrameRatesCache.reset();
 
     mDisplayModes = std::move(modes);
-    mActiveModeIt = mDisplayModes.find(activeModeId);
-    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
+    const auto activeModeOpt = mDisplayModes.get(activeModeId);
+    LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+    mActiveModeOpt =
+            FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())};
 
     const auto sortedModes = sortByRefreshRate(mDisplayModes);
     mMinRefreshRateModeIt = sortedModes.front();
@@ -1056,14 +1058,19 @@
     const auto& primaryRanges = policy.primaryRanges;
     const auto& appRequestRanges = policy.appRequestRanges;
     ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
-             "Physical range is invalid");
-    ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), "Render range is invalid");
+             "Physical range is invalid: primary: %s appRequest: %s",
+             to_string(primaryRanges.physical).c_str(),
+             to_string(appRequestRanges.physical).c_str());
+    ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render),
+             "Render range is invalid: primary: %s appRequest: %s",
+             to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str());
 
     return primaryRanges.valid() && appRequestRanges.valid();
 }
 
 auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
     Policy oldPolicy;
+    PhysicalDisplayId displayId;
     {
         std::lock_guard lock(mLock);
         oldPolicy = *getCurrentPolicyLocked();
@@ -1103,9 +1110,10 @@
             return SetPolicyResult::Unchanged;
         }
         constructAvailableRefreshRates();
+
+        displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId();
     }
 
-    const auto displayId = getActiveMode().getPhysicalDisplayId();
     const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
 
     ALOGI("Display %s policy changed\n"
@@ -1132,12 +1140,10 @@
     return mDisplayManagerPolicy;
 }
 
-bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const {
+bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const {
     std::lock_guard lock(mLock);
-    return std::any_of(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(),
-                       [modeId](const FrameRateMode& frameRateMode) {
-                           return frameRateMode.modePtr->getId() == modeId;
-                       });
+    return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) !=
+            mAppRequestFrameRates.end();
 }
 
 void RefreshRateSelector::constructAvailableRefreshRates() {
@@ -1159,8 +1165,8 @@
 
         const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
         LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
-                            "No matching frame rate modes for %s physicalRange %s", rangeName,
-                            to_string(ranges.physical).c_str());
+                            "No matching frame rate modes for %s range. policy: %s", rangeName,
+                            policy->toString().c_str());
 
         const auto stringifyModes = [&] {
             std::string str;
@@ -1211,7 +1217,7 @@
     }
 
     const DisplayModePtr& maxByPolicy =
-            getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+            getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup());
     if (minByPolicy == maxByPolicy) {
         // Turn on the timer when the min of the primary range is below the device min.
         if (const Policy* currentPolicy = getCurrentPolicyLocked();
@@ -1257,8 +1263,8 @@
 
     std::lock_guard lock(mLock);
 
-    const auto activeModeId = getActiveModeItLocked()->first;
-    dumper.dump("activeModeId"sv, std::to_string(activeModeId.value()));
+    const auto activeMode = getActiveModeLocked();
+    dumper.dump("activeMode"sv, to_string(activeMode));
 
     dumper.dump("displayModes"sv);
     {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 89ebeea..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -133,7 +133,7 @@
     Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
 
     // Returns true if mode is allowed by the current policy.
-    bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
+    bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock);
 
     // Describes the different options the layer voted for refresh rate
     enum class LayerVoteType {
@@ -240,14 +240,14 @@
         return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
     }
 
-    std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
-                                            bool timerExpired) const EXCLUDES(mLock);
+    ftl::Optional<FrameRateMode> onKernelTimerChanged(
+            std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+            EXCLUDES(mLock);
 
-    void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+    void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
 
-    // See mActiveModeIt for thread safety.
-    DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
-    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
+    // See mActiveModeOpt for thread safety.
+    FrameRateMode getActiveMode() const EXCLUDES(mLock);
 
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -399,8 +399,8 @@
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
-    // See mActiveModeIt for thread safety.
-    DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+    // See mActiveModeOpt for thread safety.
+    const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
 
     RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
                                                GlobalSignals signals) const REQUIRES(mLock);
@@ -478,9 +478,7 @@
     // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
     ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
 
-    // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
-    // need not be under mLock.
-    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+    ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock);
 
     DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
     DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0c541f9..34f8df2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -174,7 +174,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+        const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -282,7 +282,7 @@
     thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
 }
 
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         // Cache the last reported modes for primary display.
@@ -297,7 +297,7 @@
 
 void Scheduler::dispatchCachedReportedMode() {
     // Check optional fields first.
-    if (!mPolicy.mode) {
+    if (!mPolicy.modeOpt) {
         ALOGW("No mode ID found, not dispatching cached mode.");
         return;
     }
@@ -309,21 +309,21 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
+    if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
         return;
     }
 
     // If there is no change from cached mode, there is no need to dispatch an event
-    if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
+    if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
         return;
     }
 
-    mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+    mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
     onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
                                    mPolicy.cachedModeChangedParams->mode);
 }
 
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -395,6 +395,24 @@
     setVsyncPeriod(refreshRate.getPeriodNsecs());
 }
 
+void Scheduler::setRenderRate(Fps renderFrameRate) {
+    const auto mode = leaderSelectorPtr()->getActiveMode();
+
+    using fps_approx_ops::operator!=;
+    LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
+                        "Mismatch in render frame rates. Selector: %s, Scheduler: %s",
+                        to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str());
+
+    ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
+          to_string(mode.modePtr->getFps()).c_str());
+
+    const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps);
+    LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(),
+                        to_string(mode.fps).c_str());
+
+    mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor));
+}
+
 void Scheduler::resync() {
     static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
 
@@ -402,7 +420,7 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+        const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
         resyncToHardwareVsync(false, refreshRate);
     }
 }
@@ -517,7 +535,7 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+    const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -657,7 +675,7 @@
         currentState = std::forward<T>(newState);
 
         DisplayModeChoiceMap modeChoices;
-        DisplayModePtr modePtr;
+        ftl::Optional<FrameRateMode> modeOpt;
         {
             std::scoped_lock lock(mDisplayLock);
             ftl::FakeGuard guard(kMainThreadContext);
@@ -666,10 +684,10 @@
 
             // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
             // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
-            std::tie(modePtr, consideredSignals) =
+            std::tie(modeOpt, consideredSignals) =
                     modeChoices.get(*mLeaderDisplayId)
                             .transform([](const DisplayModeChoice& choice) {
-                                return std::make_pair(choice.modePtr, choice.consideredSignals);
+                                return std::make_pair(choice.mode, choice.consideredSignals);
                             })
                             .value();
         }
@@ -677,15 +695,14 @@
         modeRequests.reserve(modeChoices.size());
         for (auto& [id, choice] : modeChoices) {
             modeRequests.emplace_back(
-                    display::DisplayModeRequest{.modePtr =
-                                                        ftl::as_non_null(std::move(choice.modePtr)),
+                    display::DisplayModeRequest{.mode = std::move(choice.mode),
                                                 .emitEvent = !choice.consideredSignals.idle});
         }
 
-        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modePtr->getFps());
+        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
 
-        if (mPolicy.mode != modePtr) {
-            mPolicy.mode = modePtr;
+        if (mPolicy.modeOpt != modeOpt) {
+            mPolicy.modeOpt = modeOpt;
             refreshRateChanged = true;
         } else {
             // We don't need to change the display mode, but we might need to send an event
@@ -725,12 +742,11 @@
     const auto globalSignals = makeGlobalSignals();
 
     for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
-        auto rankedRefreshRates =
+        auto rankedFrameRates =
                 selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
 
-        for (const auto& [frameRateMode, score] : rankedRefreshRates.ranking) {
-            const auto& modePtr = frameRateMode.modePtr;
-            const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score);
+        for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
+            const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
 
             if (!inserted) {
                 auto& tally = it->second;
@@ -739,7 +755,7 @@
             }
         }
 
-        perDisplayRanking.push_back(std::move(rankedRefreshRates));
+        perDisplayRanking.push_back(std::move(rankedFrameRates));
     }
 
     auto maxScoreIt = refreshRateTallies.cbegin();
@@ -773,17 +789,15 @@
     for (auto& [ranking, signals] : perDisplayRanking) {
         if (!chosenFps) {
             const auto& [frameRateMode, _] = ranking.front();
-            const auto& modePtr = frameRateMode.modePtr;
-            modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
-                                    DisplayModeChoice{modePtr, signals});
+            modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+                                    DisplayModeChoice{frameRateMode, signals});
             continue;
         }
 
         for (auto& [frameRateMode, _] : ranking) {
-            const auto& modePtr = frameRateMode.modePtr;
-            if (modePtr->getFps() == *chosenFps) {
-                modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
-                                        DisplayModeChoice{modePtr, signals});
+            if (frameRateMode.fps == *chosenFps) {
+                modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+                                        DisplayModeChoice{frameRateMode, signals});
                 break;
             }
         }
@@ -801,18 +815,18 @@
             .powerOnImminent = powerOnImminent};
 }
 
-DisplayModePtr Scheduler::getPreferredDisplayMode() {
+ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure the stored mode is up to date.
-    if (mPolicy.mode) {
+    if (mPolicy.modeOpt) {
         const auto ranking =
                 leaderSelectorPtr()
                         ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
                         .ranking;
 
-        mPolicy.mode = ranking.front().frameRateMode.modePtr;
+        mPolicy.modeOpt = ranking.front().frameRateMode;
     }
-    return mPolicy.mode;
+    return mPolicy.modeOpt;
 }
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index fb23071..cf2ffb8 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -149,8 +149,8 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
+    void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
@@ -161,6 +161,9 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
+    // Sets the render rate for the scheduler to run at.
+    void setRenderRate(Fps);
+
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
 
@@ -207,7 +210,7 @@
     void dumpVsync(std::string&) const;
 
     // Get the appropriate refresh for current conditions.
-    DisplayModePtr getPreferredDisplayMode();
+    ftl::Optional<FrameRateMode> getPreferredDisplayMode();
 
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -235,7 +238,7 @@
     std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
     nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
-        return leaderSelectorPtr()->getActiveModePtr()->getFps().getPeriodNsecs();
+        return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -283,19 +286,19 @@
     GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
 
     struct DisplayModeChoice {
-        DisplayModeChoice(DisplayModePtr modePtr, GlobalSignals consideredSignals)
-              : modePtr(std::move(modePtr)), consideredSignals(consideredSignals) {}
+        DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
+              : mode(std::move(mode)), consideredSignals(consideredSignals) {}
 
-        DisplayModePtr modePtr;
+        FrameRateMode mode;
         GlobalSignals consideredSignals;
 
         bool operator==(const DisplayModeChoice& other) const {
-            return modePtr == other.modePtr && consideredSignals == other.consideredSignals;
+            return mode == other.mode && consideredSignals == other.consideredSignals;
         }
 
         // For tests.
         friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) {
-            return stream << '{' << to_string(*choice.modePtr) << " considering "
+            return stream << '{' << to_string(*choice.mode.modePtr) << " considering "
                           << choice.consideredSignals.toString().c_str() << '}';
         }
     };
@@ -382,11 +385,11 @@
         hal::PowerMode displayPowerMode = hal::PowerMode::ON;
 
         // Chosen display mode.
-        DisplayModePtr mode;
+        ftl::Optional<FrameRateMode> modeOpt;
 
         struct ModeChangedParams {
             ConnectionHandle handle;
-            DisplayModePtr mode;
+            FrameRateMode mode;
         };
 
         // Parameters for latest dispatch of mode change event.
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 0ad4236..ed4d25e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -253,7 +253,13 @@
 
 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
     std::lock_guard lock(mMutex);
-    return nextAnticipatedVSyncTimeFromLocked(timePoint);
+
+    // TODO(b/246164114): This implementation is not efficient at all. Refactor.
+    nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint);
+    while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) {
+        nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1);
+    }
+    return nextVsync;
 }
 
 /*
@@ -265,6 +271,13 @@
  * isVSyncInPhase(50.0, 30) = true
  */
 bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
+    std::lock_guard lock(mMutex);
+    const auto divisor =
+            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+    return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
+}
+
+bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
     struct VsyncError {
         nsecs_t vsyncTimestamp;
         float error;
@@ -272,9 +285,6 @@
         bool operator<(const VsyncError& other) const { return error < other.error; }
     };
 
-    std::lock_guard lock(mMutex);
-    const auto divisor =
-            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
     if (divisor <= 1 || timePoint == 0) {
         return true;
     }
@@ -312,6 +322,12 @@
     return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
 }
 
+void VSyncPredictor::setDivisor(unsigned divisor) {
+    ALOGV("%s: %d", __func__, divisor);
+    std::lock_guard lock(mMutex);
+    mDivisor = divisor;
+}
+
 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
     std::lock_guard lock(mMutex);
     const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3181102..4a3ba67 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -67,6 +67,8 @@
 
     bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
 
+    void setDivisor(unsigned divisor) final EXCLUDES(mMutex);
+
     void dump(std::string& result) const final EXCLUDES(mMutex);
 
 private:
@@ -89,6 +91,8 @@
 
     nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
 
+    bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
+
     nsecs_t mIdealPeriod GUARDED_BY(mMutex);
     std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
 
@@ -100,6 +104,8 @@
 
     size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+
+    unsigned mDivisor GUARDED_BY(mMutex) = 1;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 76315d2..8d1629f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -79,6 +79,17 @@
      */
     virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
 
+    /*
+     * Sets a divisor on the rate (which is a multiplier of the period).
+     * The tracker will continue to track the vsync timeline and expect it
+     * to match the current period, however, nextAnticipatedVSyncTimeFrom will
+     * return vsyncs according to the divisor set. Setting a divisor is useful
+     * when a display is running at 120Hz but the render frame rate is 60Hz.
+     *
+     * \param [in] divisor   The rate divisor the tracker should operate at.
+     */
+    virtual void setDivisor(unsigned divisor) = 0;
+
     virtual void dump(std::string& result) const = 0;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
index 670ab45..db38ebe 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <ftl/non_null.h>
 #include <scheduler/Fps.h>
 
 // TODO(b/241285191): Pull this to <ui/DisplayMode.h>
@@ -25,7 +26,7 @@
 
 struct FrameRateMode {
     Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
-    DisplayModePtr modePtr;
+    ftl::NonNull<DisplayModePtr> modePtr;
 
     bool operator==(const FrameRateMode& other) const {
         return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
@@ -35,10 +36,7 @@
 };
 
 inline std::string to_string(const FrameRateMode& mode) {
-    if (mode.modePtr) {
-        return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
-    }
-    return "{invalid}";
+    return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
 }
 
-} // namespace android::scheduler
\ No newline at end of file
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0d5b4c6..80a8bb6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -198,6 +198,9 @@
 
 namespace {
 
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
+
 // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
 constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
 
@@ -245,6 +248,44 @@
     return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
 }
 
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+    using fps_approx_ops::operator>;
+    Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+    return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+            outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+                                     ui::DisplayMode& outMode) {
+    if (isAbove4k30(outMode) &&
+        std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                    [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+        for (ui::Hdr type : displayHdrTypes) {
+            if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+                outMode.supportedHdrTypes.push_back(type);
+            }
+        }
+    } else {
+        for (ui::Hdr type : displayHdrTypes) {
+            if (type != ui::Hdr::DOLBY_VISION_4K30) {
+                outMode.supportedHdrTypes.push_back(type);
+            }
+        }
+    }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+    std::vector<ui::Hdr> hdrTypes;
+    for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+        if (type != ui::Hdr::DOLBY_VISION_4K30) {
+            hdrTypes.push_back(type);
+        }
+    }
+    return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+            displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+            displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
 }  // namespace anonymous
 
 // ---------------------------------------------------------------------------
@@ -422,7 +463,9 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
-    mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+    mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+    mRefreshRateOverlayRenderRate =
+            property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
 
     if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
         mTransactionTracing.emplace();
@@ -846,8 +889,6 @@
         }
     }
 
-    onActiveDisplaySizeChanged(display);
-
     // Inform native graphics APIs whether the present timestamp is supported:
 
     const bool presentFenceReliable =
@@ -933,17 +974,14 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
-                                              ui::StaticDisplayInfo* info) {
-    if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+    if (!info) {
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mStateLock);
-
-    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
-                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
-                                    .and_then(getDisplayDeviceAndSnapshot());
+    const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+    const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
 
     if (!displayOpt) {
         return NAME_NOT_FOUND;
@@ -970,26 +1008,10 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
-                                               ui::DynamicDisplayInfo* info) {
-    if (!displayToken || !info) {
-        return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mStateLock);
-
-    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
-                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
-                                    .and_then(getDisplayDeviceAndSnapshot());
-    if (!displayOpt) {
-        return NAME_NOT_FOUND;
-    }
-
-    const auto& [display, snapshotRef] = *displayOpt;
-    const auto& snapshot = snapshotRef.get();
-
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+                                                   const sp<DisplayDevice>& display,
+                                                   const display::DisplaySnapshot& snapshot) {
     const auto& displayModes = snapshot.displayModes();
-
     info->supportedDisplayModes.clear();
     info->supportedDisplayModes.reserve(displayModes.size());
 
@@ -1033,7 +1055,8 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+        excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+                                        outMode);
         info->supportedDisplayModes.push_back(outMode);
     }
 
@@ -1041,9 +1064,11 @@
 
     const PhysicalDisplayId displayId = snapshot.displayId();
 
-    info->activeDisplayModeId = display->refreshRateSelector().getActiveModePtr()->getId().value();
+    const auto mode = display->refreshRateSelector().getActiveMode();
+    info->activeDisplayModeId = mode.modePtr->getId().value();
+    info->renderFrameRate = mode.fps.getValue();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    info->hdrCapabilities = display->getHdrCapabilities();
+    info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
     info->autoLowLatencyModeSupported =
             getHwComposer().hasDisplayCapability(displayId,
@@ -1060,7 +1085,47 @@
             }
         }
     }
+}
 
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+                                                     ui::DynamicDisplayInfo* info) {
+    if (!info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto id_ =
+            DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+    const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+    if (!displayOpt) {
+        return NAME_NOT_FOUND;
+    }
+
+    const auto& [display, snapshotRef] = *displayOpt;
+    getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                                        ui::DynamicDisplayInfo* info) {
+    if (!displayToken || !info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                    .and_then(getDisplayDeviceAndSnapshot());
+
+    if (!displayOpt) {
+        return NAME_NOT_FOUND;
+    }
+
+    const auto& [display, snapshotRef] = *displayOpt;
+    getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
     return NO_ERROR;
 }
 
@@ -1078,26 +1143,40 @@
 void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
     ATRACE_CALL();
 
-    auto display = getDisplayDeviceLocked(request.modePtr->getPhysicalDisplayId());
+    auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
     if (!display) {
         ALOGW("%s: display is no longer valid", __func__);
         return;
     }
 
-    const Fps refreshRate = request.modePtr->getFps();
+    const auto mode = request.mode;
+    const bool emitEvent = request.emitEvent;
 
-    if (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
-        scheduleComposite(FrameHint::kNone);
+    switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
+        case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
+            scheduleComposite(FrameHint::kNone);
 
-        // Start receiving vsync samples now, so that we can detect a period
-        // switch.
-        mScheduler->resyncToHardwareVsync(true, refreshRate);
-        // As we called to set period, we will call to onRefreshRateChangeCompleted once
-        // VsyncController model is locked.
-        modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+            // Start receiving vsync samples now, so that we can detect a period
+            // switch.
+            mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
+            // As we called to set period, we will call to onRefreshRateChangeCompleted once
+            // VsyncController model is locked.
+            modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
-        updatePhaseConfiguration(refreshRate);
-        mScheduler->setModeChangePending(true);
+            updatePhaseConfiguration(mode.fps);
+            mScheduler->setModeChangePending(true);
+            break;
+        case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
+            mScheduler->setRenderRate(mode.fps);
+            updatePhaseConfiguration(mode.fps);
+            mRefreshRateStats->setRefreshRate(mode.fps);
+            if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+                mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+            }
+
+            break;
+        case DisplayDevice::DesiredActiveModeAction::None:
+            break;
     }
 }
 
@@ -1159,18 +1238,19 @@
     }
 
     const auto upcomingModeInfo = display->getUpcomingActiveMode();
-    if (!upcomingModeInfo.mode) {
+    if (!upcomingModeInfo.modeOpt) {
         // There is no pending mode change. This can happen if the active
         // display changed and the mode change happened on a different display.
         return;
     }
 
-    if (display->getActiveMode().getResolution() != upcomingModeInfo.mode->getResolution()) {
+    if (display->getActiveMode().modePtr->getResolution() !=
+        upcomingModeInfo.modeOpt->modePtr->getResolution()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
         state.sequenceId = DisplayDeviceState{}.sequenceId;
-        state.physical->activeMode = upcomingModeInfo.mode;
+        state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get();
         processDisplayChangesLocked();
 
         // processDisplayChangesLocked will update all necessary components so we're done here.
@@ -1181,15 +1261,17 @@
             .transform(&PhysicalDisplay::snapshotRef)
             .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
                 FTL_FAKE_GUARD(kMainThreadContext,
-                               display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot));
+                               display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
+                                                      upcomingModeInfo.modeOpt->modePtr->getFps(),
+                                                      upcomingModeInfo.modeOpt->fps));
             }));
 
-    const Fps refreshRate = upcomingModeInfo.mode->getFps();
+    const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
     mRefreshRateStats->setRefreshRate(refreshRate);
     updatePhaseConfiguration(refreshRate);
 
     if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
     }
 }
 
@@ -1201,10 +1283,12 @@
 }
 
 void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
-    const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
+    const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps();
+    const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps;
     clearDesiredActiveModeState(display);
-    mScheduler->resyncToHardwareVsync(true, refreshRate);
-    updatePhaseConfiguration(refreshRate);
+    mScheduler->resyncToHardwareVsync(true, displayFps);
+    mScheduler->setRenderRate(renderFps);
+    updatePhaseConfiguration(renderFps);
 }
 
 void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
@@ -1235,13 +1319,10 @@
             continue;
         }
 
-        const auto desiredModeId = desiredActiveMode->mode->getId();
-        const auto refreshRateOpt =
-                snapshot.displayModes()
-                        .get(desiredModeId)
-                        .transform([](const DisplayModePtr& mode) { return mode->getFps(); });
+        const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
+        const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
 
-        if (!refreshRateOpt) {
+        if (!displayModePtrOpt) {
             ALOGW("Desired display mode is no longer supported. Mode ID = %d",
                   desiredModeId.value());
             clearDesiredActiveModeState(display);
@@ -1249,9 +1330,10 @@
         }
 
         ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
-              to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str());
+              to_string(displayModePtrOpt->get()->getFps()).c_str(),
+              to_string(display->getId()).c_str());
 
-        if (display->getActiveMode().getId() == desiredModeId) {
+        if (display->getActiveMode() == desiredActiveMode->modeOpt) {
             // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
@@ -1260,7 +1342,8 @@
         // Desired active mode was set, it is different than the mode currently in use, however
         // allowed modes might have changed by the time we process the refresh.
         // Make sure the desired mode is still allowed
-        const auto displayModeAllowed = display->refreshRateSelector().isModeAllowed(desiredModeId);
+        const auto displayModeAllowed =
+                display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt);
         if (!displayModeAllowed) {
             clearDesiredActiveModeState(display);
             continue;
@@ -1301,8 +1384,7 @@
 
         const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
         const auto desiredActiveMode = display->getDesiredActiveMode();
-        if (desiredActiveMode &&
-            display->getActiveMode().getId() == desiredActiveMode->mode->getId()) {
+        if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
             desiredActiveModeChangeDone(display);
         }
     }
@@ -1497,7 +1579,8 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+                                    bool* success) {
     *success = mTimeStats->onPullAtom(atomId, pulledData);
     return NO_ERROR;
 }
@@ -1825,7 +1908,7 @@
     if (hint == FrameHint::kActive) {
         mScheduler->resetIdleTimer();
     }
-    mPowerAdvisor->notifyDisplayUpdateImminent();
+    mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
     mScheduler->scheduleFrame();
 }
 
@@ -2071,7 +2154,7 @@
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
         const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
-        const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod());
+        const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs());
         mPowerAdvisor->setCommitStart(frameTime);
         mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
 
@@ -2815,7 +2898,7 @@
                 return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
             }
 
-            if (!base::GetBoolProperty("debug.sf.frame_rate_override_global"s, false)) {
+            if (!sysprop::frame_rate_override_global(false)) {
                 return Config::FrameRateOverride::AppOverride;
             }
 
@@ -2900,7 +2983,9 @@
                 .transform(&PhysicalDisplay::snapshotRef)
                 .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
                     FTL_FAKE_GUARD(kMainThreadContext,
-                                   display->setActiveMode(physical->activeMode->getId(), snapshot));
+                                   display->setActiveMode(physical->activeMode->getId(),
+                                                          physical->activeMode->getFps(),
+                                                          physical->activeMode->getFps()));
                 }));
     }
 
@@ -3096,7 +3181,7 @@
 
 void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->getActiveMode().getFps();
+    const Fps refreshRate = activeDisplay->getActiveMode().fps;
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3378,12 +3463,12 @@
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
     for (auto& request : modeRequests) {
-        const auto& modePtr = request.modePtr;
+        const auto& modePtr = request.mode.modePtr;
         const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
 
         if (!display) continue;
 
-        if (display->refreshRateSelector().isModeAllowed(modePtr->getId())) {
+        if (display->refreshRateSelector().isModeAllowed(request.mode)) {
             setDesiredActiveMode(std::move(request));
         } else {
             ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
@@ -3404,8 +3489,8 @@
 void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
     LOG_ALWAYS_FATAL_IF(mScheduler);
 
-    const auto activeModePtr = display->refreshRateSelector().getActiveModePtr();
-    const Fps activeRefreshRate = activeModePtr->getFps();
+    const auto activeMode = display->refreshRateSelector().getActiveMode();
+    const Fps activeRefreshRate = activeMode.fps;
     mRefreshRateStats =
             std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
                                                           hal::PowerMode::OFF);
@@ -3457,14 +3542,6 @@
             sp<RegionSamplingThread>::make(*this,
                                            RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
-    // Dispatch a mode change request for the primary display on scheduler
-    // initialization, so that the EventThreads always contain a reference to a
-    // prior configuration.
-    //
-    // This is a bit hacky, but this avoids a back-pointer into the main SF
-    // classes from EventThread, and there should be no run-time binder cost
-    // anyway since there are no connected apps at this point.
-    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
 }
 
 void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3981,7 +4058,7 @@
 
 bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                            std::vector<ResolvedComposerState>& states,
-                                           const Vector<DisplayState>& displays, uint32_t flags,
+                                           Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
                                            const client_cache_t& uncacheBuffer,
@@ -3990,7 +4067,8 @@
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
-    for (const DisplayState& display : displays) {
+    for (DisplayState& display : displays) {
+        display.sanitize(permissions);
         transactionFlags |= setDisplayStateLocked(display);
     }
 
@@ -4647,8 +4725,6 @@
                           {}, mPid, getuid(), transactionId);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
-
-    mActiveDisplayTransformHint = display->getTransformHint();
 }
 
 void SurfaceFlinger::initializeDisplays() {
@@ -4667,8 +4743,8 @@
     const auto displayId = display->getPhysicalId();
     ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
-    std::optional<hal::PowerMode> currentMode = display->getPowerMode();
-    if (currentMode.has_value() && mode == *currentMode) {
+    const auto currentModeOpt = display->getPowerMode();
+    if (currentModeOpt == mode) {
         return;
     }
 
@@ -4678,18 +4754,18 @@
                                            .value_or(false);
 
     const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
-    if (isInternalDisplay && activeDisplay != display && activeDisplay &&
-        activeDisplay->isPoweredOn()) {
-        ALOGW("Trying to change power mode on non active display while the active display is ON");
-    }
+    const bool isActiveDisplayPoweredOn = activeDisplay && activeDisplay->isPoweredOn();
+
+    ALOGW_IF(display != activeDisplay && isInternalDisplay && isActiveDisplayPoweredOn,
+             "Trying to change power mode on inactive display without powering off active display");
 
     display->setPowerMode(mode);
 
-    const auto refreshRate = display->refreshRateSelector().getActiveMode().getFps();
-    if (*currentMode == hal::PowerMode::OFF) {
+    const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
+    if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
         // Turn on the display
-        if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
-            onActiveDisplayChangedLocked(display);
+        if (isInternalDisplay && !isActiveDisplayPoweredOn) {
+            onActiveDisplayChangedLocked(activeDisplay, display);
         }
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
         // We can merge the syscall later.
@@ -4716,7 +4792,7 @@
         if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
             ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
         }
-        if (isActiveDisplay && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4730,7 +4806,7 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (isActiveDisplay && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
             ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
             mVisibleRegionsDirty = true;
             scheduleRepaint();
@@ -5255,7 +5331,8 @@
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         std::string fps, xDpi, yDpi;
-        if (const auto activeModePtr = display->refreshRateSelector().getActiveModePtr()) {
+        if (const auto activeModePtr =
+                    display->refreshRateSelector().getActiveMode().modePtr.get()) {
             fps = to_string(activeModePtr->getFps());
 
             const auto dpi = activeModePtr->getDpi();
@@ -5927,7 +6004,7 @@
     if (!updateOverlay) return;
 
     // Update the overlay on the main thread to avoid race conditions with
-    // RefreshRateSelector::getActiveMode.
+    // RefreshRateSelector::getActiveMode
     static_cast<void>(mScheduler->schedule([=] {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
         if (!display) {
@@ -5938,7 +6015,8 @@
 
         const auto desiredActiveMode = display->getDesiredActiveMode();
         const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
-                ? std::make_optional(desiredActiveMode->mode->getId())
+                ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId())
+
                 : std::nullopt;
 
         const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -6632,11 +6710,11 @@
     }
 }
 
-std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayMode(
+ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
         PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
     if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
-        schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
-        return ftl::as_non_null(schedulerMode);
+        schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) {
+        return schedulerMode;
     }
 
     return mPhysicalDisplays.get(displayId)
@@ -6644,7 +6722,9 @@
             .and_then([&](const display::DisplaySnapshot& snapshot) {
                 return snapshot.displayModes().get(defaultModeId);
             })
-            .transform(&ftl::as_non_null<const DisplayModePtr&>);
+            .transform([](const DisplayModePtr& modePtr) {
+                return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+            });
 }
 
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
@@ -6668,19 +6748,22 @@
         case SetPolicyResult::Unchanged:
             return NO_ERROR;
         case SetPolicyResult::Changed:
-            break;
+            return applyRefreshRateSelectorPolicy(displayId, selector);
     }
+}
 
+status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
+        PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
     const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    if (const auto activeModePtr = selector.getActiveModePtr(); displayId == mActiveDisplayId) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+    if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
         toggleKernelIdleTimer();
     } else {
-        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
     }
 
     auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -6690,12 +6773,12 @@
     }
 
     auto preferredMode = std::move(*preferredModeOpt);
-    const auto preferredModeId = preferredMode->getId();
+    const auto preferredModeId = preferredMode.modePtr->getId();
 
     ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
-          to_string(preferredMode->getFps()).c_str());
+          to_string(preferredMode.fps).c_str());
 
-    if (!selector.isModeAllowed(preferredModeId)) {
+    if (!selector.isModeAllowed(preferredMode)) {
         ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
         return INVALID_OPERATION;
     }
@@ -6868,7 +6951,8 @@
     for (const auto& [id, display] : mPhysicalDisplays) {
         if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
             if (const auto device = getDisplayDeviceLocked(id)) {
-                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+                                                 mRefreshRateOverlayRenderRate);
             }
         }
     }
@@ -6907,7 +6991,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
+            refreshRate = display->refreshRateSelector().getActiveMode().fps;
         }
     }
 
@@ -6981,17 +7065,14 @@
     getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
 }
 
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+                                                  const sp<DisplayDevice>& activeDisplay) {
     ATRACE_CALL();
 
-    if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
-        display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+    if (inactiveDisplay) {
+        inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
     }
 
-    if (!activeDisplay) {
-        ALOGE("%s: activeDisplay is null", __func__);
-        return;
-    }
     mActiveDisplayId = activeDisplay->getPhysicalId();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
 
@@ -7002,9 +7083,11 @@
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay->getTransformHint();
 
-    // Update the kernel timer for the current active display, since the policy
-    // for this display might have changed when it was not the active display.
-    toggleKernelIdleTimer();
+    // The policy of the new active/leader display may have changed while it was inactive. In that
+    // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
+    // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
+    // and the kernel idle timer of the newly active display must be toggled.
+    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector());
 }
 
 status_t SurfaceFlinger::addWindowInfosListener(
@@ -7030,8 +7113,7 @@
                                    layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
         ALOGD("%s", errorMessage.c_str());
         if (bufferData.releaseBufferListener) {
-            bufferData.releaseBufferListener->onTransactionQueueStalled(
-                    String8(errorMessage.c_str()));
+            bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
         }
         return nullptr;
     }
@@ -7049,7 +7131,7 @@
 
             if (bufferData.releaseBufferListener) {
                 bufferData.releaseBufferListener->onTransactionQueueStalled(
-                        String8("Buffer processing hung due to full buffer cache"));
+                        "Buffer processing hung due to full buffer cache");
             }
         }
 
@@ -7223,6 +7305,10 @@
 
 binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
                                                             sp<IBinder>* outDisplay) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
     *outDisplay = mFlinger->getPhysicalDisplayToken(*id);
     return binder::Status::ok();
@@ -7273,11 +7359,12 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(const sp<IBinder>& display,
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
                                                          gui::StaticDisplayInfo* outInfo) {
     using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
     ui::StaticDisplayInfo info;
-    status_t status = mFlinger->getStaticDisplayInfo(display, &info);
+
+    status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
     if (status == NO_ERROR) {
         // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
         outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
@@ -7316,53 +7403,71 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::getDynamicDisplayInfo(const sp<IBinder>& display,
-                                                          gui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+                                                        gui::DynamicDisplayInfo*& outInfo) {
+    // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+    outInfo->supportedDisplayModes.clear();
+    outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+    for (const auto& mode : info.supportedDisplayModes) {
+        gui::DisplayMode outMode;
+        outMode.id = mode.id;
+        outMode.resolution.width = mode.resolution.width;
+        outMode.resolution.height = mode.resolution.height;
+        outMode.xDpi = mode.xDpi;
+        outMode.yDpi = mode.yDpi;
+        outMode.refreshRate = mode.refreshRate;
+        outMode.appVsyncOffset = mode.appVsyncOffset;
+        outMode.sfVsyncOffset = mode.sfVsyncOffset;
+        outMode.presentationDeadline = mode.presentationDeadline;
+        outMode.group = mode.group;
+        std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+                       std::back_inserter(outMode.supportedHdrTypes),
+                       [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+        outInfo->supportedDisplayModes.push_back(outMode);
+    }
+
+    outInfo->activeDisplayModeId = info.activeDisplayModeId;
+    outInfo->renderFrameRate = info.renderFrameRate;
+
+    outInfo->supportedColorModes.clear();
+    outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+    for (const auto& cmode : info.supportedColorModes) {
+        outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+    }
+
+    outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+    gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+    hdrCapabilities.supportedHdrTypes.clear();
+    hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+    for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+        hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+    }
+    hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+    hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+    hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+    outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+    outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+    outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+        const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
     ui::DynamicDisplayInfo info;
-    status_t status = mFlinger->getDynamicDisplayInfo(display, &info);
+    status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
     if (status == NO_ERROR) {
-        // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
-        outInfo->supportedDisplayModes.clear();
-        outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
-        for (const auto& mode : info.supportedDisplayModes) {
-            gui::DisplayMode outMode;
-            outMode.id = mode.id;
-            outMode.resolution.width = mode.resolution.width;
-            outMode.resolution.height = mode.resolution.height;
-            outMode.xDpi = mode.xDpi;
-            outMode.yDpi = mode.yDpi;
-            outMode.refreshRate = mode.refreshRate;
-            outMode.appVsyncOffset = mode.appVsyncOffset;
-            outMode.sfVsyncOffset = mode.sfVsyncOffset;
-            outMode.presentationDeadline = mode.presentationDeadline;
-            outMode.group = mode.group;
-            outInfo->supportedDisplayModes.push_back(outMode);
-        }
+        getDynamicDisplayInfoInternal(info, outInfo);
+    }
+    return binderStatusFromStatusT(status);
+}
 
-        outInfo->activeDisplayModeId = info.activeDisplayModeId;
-
-        outInfo->supportedColorModes.clear();
-        outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
-        for (const auto& cmode : info.supportedColorModes) {
-            outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
-        }
-
-        outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
-
-        gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
-        hdrCapabilities.supportedHdrTypes.clear();
-        hdrCapabilities.supportedHdrTypes.reserve(
-                info.hdrCapabilities.getSupportedHdrTypes().size());
-        for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
-            hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
-        }
-        hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
-        hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
-        hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
-
-        outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
-        outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
-        outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+                                                                gui::DynamicDisplayInfo* outInfo) {
+    ui::DynamicDisplayInfo info;
+    status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+    if (status == NO_ERROR) {
+        getDynamicDisplayInfoInternal(info, outInfo);
     }
     return binderStatusFromStatusT(status);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 50e8e73..8fa4278 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,6 +26,7 @@
 #include <android/gui/DisplayStatInfo.h>
 #include <android/gui/DisplayState.h>
 #include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/ITransactionCompletedListener.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <ftl/future.h>
@@ -34,8 +35,8 @@
 #include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerDebugInfo.h>
+
 #include <gui/LayerState.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
@@ -125,7 +126,9 @@
 using gui::CaptureArgs;
 using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
+using gui::ITransactionCompletedListener;
 using gui::LayerCaptureArgs;
+
 using gui::ScreenCaptureResults;
 
 namespace frametimeline {
@@ -512,10 +515,13 @@
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
             EXCLUDES(mStateLock);
-    status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+    status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+    status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
             EXCLUDES(mStateLock);
-    status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
-            EXCLUDES(mStateLock);
+    status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                            ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+    void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+                                       const display::DisplaySnapshot&);
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
     status_t getBootDisplayModeSupport(bool* outSupport) const;
@@ -527,7 +533,7 @@
     void setPowerMode(const sp<IBinder>& displayToken, int mode);
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
                               const std::vector<ui::Hdr>& hdrTypes);
-    status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
+    status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
     status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
     status_t getColorManagement(bool* outGetColorManagement) const;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -639,6 +645,8 @@
     bool mKernelIdleTimerEnabled = false;
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
+    // Show render rate with refresh rate overlay
+    bool mRefreshRateOverlayRenderRate = false;
 
     void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
 
@@ -660,13 +668,18 @@
 
     // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
     // display. Falls back to the display's defaultModeId otherwise.
-    std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode(
+    ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode(
             PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
 
     status_t setDesiredDisplayModeSpecsInternal(
             const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
             EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
 
+    // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
+    status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
+                                            const scheduler::RefreshRateSelector&)
+            REQUIRES(mStateLock, kMainThreadContext);
+
     void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
     void commitTransactionsLocked(uint32_t transactionFlags)
             REQUIRES(mStateLock, kMainThreadContext);
@@ -695,7 +708,7 @@
      */
     bool applyTransactionState(const FrameTimelineInfo& info,
                                std::vector<ResolvedComposerState>& state,
-                               const Vector<DisplayState>& displays, uint32_t flags,
+                               Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime, bool isAutoTimestamp,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -995,7 +1008,10 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
-    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+    // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
+    // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
+    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+                                      const sp<DisplayDevice>& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
 
     void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
@@ -1391,10 +1407,12 @@
                                    gui::DisplayStatInfo* outStatInfo) override;
     binder::Status getDisplayState(const sp<IBinder>& display,
                                    gui::DisplayState* outState) override;
-    binder::Status getStaticDisplayInfo(const sp<IBinder>& display,
+    binder::Status getStaticDisplayInfo(int64_t displayId,
                                         gui::StaticDisplayInfo* outInfo) override;
-    binder::Status getDynamicDisplayInfo(const sp<IBinder>& display,
-                                         gui::DynamicDisplayInfo* outInfo) override;
+    binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+                                               gui::DynamicDisplayInfo* outInfo) override;
+    binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+                                                  gui::DynamicDisplayInfo* outInfo) override;
     binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
                                              gui::DisplayPrimaries* outPrimaries) override;
     binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
@@ -1479,6 +1497,8 @@
     status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
     status_t checkControlDisplayBrightnessPermission();
     status_t checkReadFrameBufferPermission();
+    static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+                                              gui::DynamicDisplayInfo*& outInfo);
 
 private:
     sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c8c71df..5b73030 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,6 +371,10 @@
     return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
 }
 
+bool frame_rate_override_global(bool defaultValue) {
+    return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue);
+}
+
 bool enable_layer_caching(bool defaultValue) {
     return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
 }
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5e316cf..09629cf 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -98,6 +98,8 @@
 
 bool frame_rate_override_for_native_rates(bool defaultValue);
 
+bool frame_rate_override_global(bool defaultValue);
+
 bool enable_layer_caching(bool defaultValue);
 
 bool enable_sdr_dimming(bool defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index e860d88..9368edd 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -90,7 +90,7 @@
 }
 } // namespace
 
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (mTimeStats.statsStartLegacy == 0) {
@@ -138,10 +138,11 @@
     // Always clear data.
     clearGlobalLocked();
 
-    return atomList.SerializeToString(pulledData);
+    pulledData->resize(atomList.ByteSizeLong());
+    return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
 }
 
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -229,7 +230,8 @@
     // Always clear data.
     clearLayersLocked();
 
-    return atomList.SerializeToString(pulledData);
+    pulledData->resize(atomList.ByteSizeLong());
+    return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
 }
 
 TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -245,7 +247,7 @@
     }
 }
 
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
     bool success = false;
     if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
         success = populateGlobalAtom(pulledData);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 61d7c22..1872d0e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -47,7 +47,7 @@
     virtual ~TimeStats() = default;
 
     // Process a pull request from statsd.
-    virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+    virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
 
     virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
     virtual bool isEnabled() = 0;
@@ -244,7 +244,7 @@
     TimeStats(std::optional<size_t> maxPulledLayers,
               std::optional<size_t> maxPulledHistogramBuckets);
 
-    bool onPullAtom(const int atomId, std::string* pulledData) override;
+    bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
     std::string miniDump() override;
@@ -292,8 +292,8 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    bool populateGlobalAtom(std::string* pulledData);
-    bool populateLayerAtom(std::string* pulledData);
+    bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+    bool populateLayerAtom(std::vector<uint8_t>* pulledData);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
                                             std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 61ff9bc..c09bcce 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,14 +26,27 @@
 #include <unordered_set>
 
 #include <android-base/thread_annotations.h>
+#include <android/gui/ITransactionCompletedListener.h>
+
 #include <binder/IBinder.h>
-#include <ftl/future.h>
-#include <gui/ITransactionCompletedListener.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/Fence.h>
 #include <ui/FenceResult.h>
 
 namespace android {
 
+using gui::CallbackId;
+using gui::FrameEventHistoryStats;
+using gui::IListenerHash;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
+using gui::ListenerCallbacks;
+using gui::ListenerStats;
+using gui::ReleaseCallbackId;
+using gui::TransactionStats;
+
 class CallbackHandle : public RefBase {
 public:
     CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 577f84e..81ca659 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -81,7 +81,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -231,8 +230,7 @@
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
 
-        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
-                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr));
     }
 
@@ -273,7 +271,7 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) {
         return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
@@ -421,7 +419,7 @@
 
     void onPullAtom(FuzzedDataProvider *fdp) {
         const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
-        std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+        std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
         bool success = fdp->ConsumeBool();
         mFlinger->onPullAtom(atomId, &pulledData, &success);
     }
@@ -493,14 +491,14 @@
         mFlinger->getDisplayState(display, &displayState);
     }
 
-    void getStaticDisplayInfo(sp<IBinder> &display) {
+    void getStaticDisplayInfo(int64_t displayId) {
         ui::StaticDisplayInfo staticDisplayInfo;
-        mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+        mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
     }
 
-    void getDynamicDisplayInfo(sp<IBinder> &display) {
+    void getDynamicDisplayInfo(int64_t displayId) {
         android::ui::DynamicDisplayInfo dynamicDisplayInfo;
-        mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+        mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
     }
     void getDisplayNativePrimaries(sp<IBinder> &display) {
         android::ui::DisplayPrimaries displayPrimaries;
@@ -524,7 +522,7 @@
         return ids.front();
     }
 
-    sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+    std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
         mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
         const sp<Client> client = sp<Client>::make(mFlinger);
 
@@ -551,13 +549,13 @@
 
         mFlinger->bootFinished();
 
-        return display;
+        return {display, physicalDisplayId.value};
     }
 
     void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
         FuzzedDataProvider mFdp(data, size);
 
-        sp<IBinder> display = fuzzBoot(&mFdp);
+        auto [display, displayId] = fuzzBoot(&mFdp);
 
         sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
 
@@ -565,8 +563,8 @@
 
         getDisplayStats(display);
         getDisplayState(display);
-        getStaticDisplayInfo(display);
-        getDynamicDisplayInfo(display);
+        getStaticDisplayInfo(displayId);
+        getDynamicDisplayInfo(displayId);
         getDisplayNativePrimaries(display);
 
         mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -658,8 +656,7 @@
         }
 
         mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
-        const auto fps =
-                FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateSelector->getActiveMode().getFps());
+        const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 950e6d3..7959e52 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -381,7 +381,8 @@
                                                              mFdp.ConsumeFloatingPoint<float>())}});
         refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
 
-        refreshRateSelector.setActiveModeId(modeId);
+        refreshRateSelector.setActiveMode(modeId,
+                                          Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
     }
 
     RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 88e32e1..2bc5b46 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -116,6 +116,8 @@
         return true;
     }
 
+    void setDivisor(unsigned) override {}
+
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
             return timePoint;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 28da81f..8540c3d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -447,8 +447,8 @@
 
 # Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate
 # to native display refresh rates only. Before introducing this flag, native display refresh rates
-# was the default behvaiour. With this flag we can control which behaviour we want explicitly.
-# This flag is intoruduced as a fail-safe machanism and planned to be defaulted to false.
+# was the default behaviour. With this flag we can control which behaviour we want explicitly.
+# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false.
 prop {
     api_name: "frame_rate_override_for_native_rates"
     type: Boolean
@@ -457,6 +457,16 @@
     prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
 }
 
+# Enables the frame rate override feature (enable_frame_rate_override) to
+# override the frame rate globally instead of only for individual apps.
+prop {
+    api_name: "frame_rate_override_global"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.frame_rate_override_global"
+}
+
 # Enables Layer Caching
 prop {
     api_name: "enable_layer_caching"
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0dfb80e..9338133 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -65,6 +65,10 @@
     prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
   }
   prop {
+    api_name: "frame_rate_override_global"
+    prop_name: "ro.surface_flinger.frame_rate_override_global"
+  }
+  prop {
     api_name: "has_HDR_display"
     prop_name: "ro.surface_flinger.has_HDR_display"
   }
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 1676844..4a45eb5 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -83,6 +83,15 @@
         return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     }
 
+    static std::optional<uint64_t> getFirstDisplayId() {
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) {
+            return std::nullopt;
+        }
+
+        return ids.front().value;
+    }
+
     void setupBackgroundSurface() {
         mDisplay = getFirstDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
@@ -169,29 +178,25 @@
 TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
     std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
     // Anyone can access display information.
-    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
 }
 
 TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
     // The following methods are tested with a UID that is not root, graphics,
     // or system, to show that anyone can access them.
     UIDFaker f(AID_BIN);
-    const auto display = getFirstDisplayToken();
-    ASSERT_TRUE(display != nullptr);
-
-    ui::DisplayMode mode;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
-    Vector<ui::DisplayMode> modes;
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
 }
 
 TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
-    const auto display = getFirstDisplayToken();
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     std::function<status_t()> condition = [=]() {
         ui::DynamicDisplayInfo info;
-        return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+        return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
 }
@@ -335,8 +340,10 @@
     status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
     ASSERT_EQ(NO_ERROR, error);
     bool hasWideColorMode = false;
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+    SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     const auto& colorModes = info.supportedColorModes;
     for (ColorMode colorMode : colorModes) {
         switch (colorMode) {
@@ -363,10 +370,10 @@
 }
 
 TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
-    const auto display = getFirstDisplayToken();
-    ASSERT_FALSE(display == nullptr);
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+    SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     ColorMode colorMode = info.activeColorMode;
     ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
 }
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 10dae46..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -45,6 +45,7 @@
     void SetUp() override {
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
+        mDisplayId = ids.front().value;
         mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
         ASSERT_EQ(res, NO_ERROR);
@@ -58,11 +59,14 @@
     void testSetAllowGroupSwitching(bool allowGroupSwitching);
 
     sp<IBinder> mDisplayToken;
+    uint64_t mDisplayId;
 };
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
     ui::DynamicDisplayInfo info;
-    status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+    status_t res =
+            SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+                                                               &info);
     const auto& modes = info.supportedDisplayModes;
     ASSERT_EQ(res, NO_ERROR);
     ASSERT_GT(modes.size(), 0);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 26c0d8e..7bccfa7 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -85,6 +85,7 @@
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
         "FrameRateOverrideMappingsTest.cpp",
+        "FrameRateSelectionPriorityTest.cpp",
         "FrameTimelineTest.cpp",
         "GameModeTest.cpp",
         "HWComposerTest.cpp",
@@ -92,6 +93,7 @@
         "LayerHistoryTest.cpp",
         "LayerInfoTest.cpp",
         "LayerMetadataTest.cpp",
+        "LayerHierarchyTest.cpp",
         "LayerLifecycleManagerTest.cpp",
         "LayerTest.cpp",
         "LayerTestUtils.cpp",
@@ -110,10 +112,10 @@
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
         "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
+        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateSelectorTest.cpp",
-        "RefreshRateSelectionTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 982b9ff..60ad7a3 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include "DisplayTransactionTestHelpers.h"
+#include "mock/MockFrameRateMode.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -29,6 +30,7 @@
 
 class InitiateModeChangeTest : public DisplayTransactionTest {
 public:
+    using Action = DisplayDevice::DesiredActiveModeAction;
     using Event = scheduler::DisplayModeEvent;
 
     void SetUp() override {
@@ -56,31 +58,42 @@
     static constexpr DisplayModeId kModeId90{1};
     static constexpr DisplayModeId kModeId120{2};
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
 };
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
     EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    // Setting another mode should be cached but return false
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    // Setting another mode should be cached but return None
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 }
 
 TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
 
     mDisplay->clearDesiredActiveModeState();
@@ -88,9 +101,11 @@
 }
 
 TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -101,18 +116,27 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
     ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
+TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+    EXPECT_EQ(Action::InitiateRenderRateSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
+    EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
 TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
 NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -123,21 +147,23 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 19c7d5c..d58e644 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -359,6 +359,7 @@
     }
 
     // Called by tests to inject a HWC display setup
+    template <bool kInitPowerMode = true>
     static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
         const auto displayId = DisplayVariant::DISPLAY_ID::get();
         ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -367,18 +368,21 @@
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setResolution(DisplayVariant::RESOLUTION)
                 .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
-                .setPowerMode(INIT_POWER_MODE)
+                .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
                 .inject(&test->mFlinger, test->mComposer);
     }
 
     // Called by tests to inject a HWC display setup
+    template <bool kInitPowerMode = true>
     static void injectHwcDisplay(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
                 .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
                                 Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
-                .WillOnce(Return(Error::NONE));
-        injectHwcDisplayWithNoDefaultCapabilities(test);
+        if constexpr (kInitPowerMode) {
+            EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+                    .WillOnce(Return(Error::NONE));
+        }
+        injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
     }
 
     static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index a5beaba..dd87f9d 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -633,9 +633,10 @@
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
@@ -644,9 +645,10 @@
                               .setId(DisplayModeId(5))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
@@ -655,8 +657,9 @@
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+    const Fps fps = mode->getFps() / 2;
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -669,9 +672,10 @@
                               .setId(DisplayModeId(9))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
 
     auto args = suppressConnectionEventRecorder.waitForCall();
     ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
similarity index 100%
rename from services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
rename to services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..8560902
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2022 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY)  \
+    ({                                \
+        SCOPED_TRACE("");             \
+        updateAndVerify((HIERARCHY)); \
+    })
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+    LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+    args.addToRoot = canBeRoot;
+    args.parentHandle = parent;
+    args.mirrorLayerHandle = mirror;
+    return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+    /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+    --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+    --gtest_shuffle \
+    --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public testing::Test {
+protected:
+    LayerHierarchyTest() {
+        // tree with 3 levels of children
+        // ROOT
+        // ├── 1
+        // │   ├── 11
+        // │   │   └── 111
+        // │   ├── 12
+        // │   │   ├── 121
+        // │   │   └── 122
+        // │   │       └── 1221
+        // │   └── 13
+        // └── 2
+
+        createRootLayer(1);
+        createRootLayer(2);
+        createLayer(11, 1);
+        createLayer(12, 1);
+        createLayer(13, 1);
+        createLayer(111, 11);
+        createLayer(121, 12);
+        createLayer(122, 12);
+        createLayer(1221, 122);
+        mLifecycleManager.commitChanges();
+    }
+    std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+        std::vector<uint32_t> layerIds;
+        hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+                                                  const LayerHierarchy::TraversalPath&) -> bool {
+            layerIds.emplace_back(hierarchy.getLayer()->id);
+            return true;
+        });
+        return layerIds;
+    }
+
+    std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+        std::vector<uint32_t> layerIds;
+        hierarchy.traverseInZOrder(
+                [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+                                       const LayerHierarchy::TraversalPath&) -> bool {
+                    layerIds.emplace_back(hierarchy.getLayer()->id);
+                    return true;
+                });
+        return layerIds;
+    }
+
+    void createRootLayer(uint32_t id) {
+        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+        mHandles[id] = handle;
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void createLayer(uint32_t id, uint32_t parentId) {
+        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+        mHandles[id] = handle;
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
+                           /*mirror=*/nullptr)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void reparentLayer(uint32_t id, uint32_t newParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        if (newParentId == UNASSIGNED_LAYER_ID) {
+            transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
+        } else {
+            auto parentHandle = mHandles[newParentId];
+            transactions.back().states.front().state.parentSurfaceControlForChild =
+                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+                                             static_cast<int32_t>(newParentId), "Test");
+        }
+        transactions.back().states.front().state.what = layer_state_t::eReparent;
+        transactions.back().states.front().state.surface = mHandles[id];
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        if (relativeParentId == UNASSIGNED_LAYER_ID) {
+            transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        } else {
+            auto parentHandle = mHandles[relativeParentId];
+            transactions.back().states.front().state.relativeLayerSurfaceControl =
+                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+                                             static_cast<int32_t>(relativeParentId), "test");
+            transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+        }
+        transactions.back().states.front().state.surface = mHandles[id];
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
+        auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
+        auto mirrorHandle =
+                (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
+
+        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+        mHandles[id] = handle;
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
+                           /*mirror=*/mHandles[layerToMirror])));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void updateBackgroundColor(uint32_t id, half alpha) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+        transactions.back().states.front().state.bgColorAlpha = alpha;
+        transactions.back().states.front().state.surface = mHandles[id];
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+    void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+        if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+            hierarchyBuilder.update(mLifecycleManager.getLayers(),
+                                    mLifecycleManager.getDestroyedLayers());
+        }
+        mLifecycleManager.commitChanges();
+
+        // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+        LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+        EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+                  getTraversalPath(newBuilder.getHierarchy()));
+        EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+                  getTraversalPathInZOrder(newBuilder.getHierarchy()));
+        EXPECT_FALSE(
+                mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    }
+
+    void setZ(uint32_t id, int32_t z) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().state.z = z;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+    LayerLifecycleManager mLifecycleManager;
+    std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    createRootLayer(3);
+    createLayer(112, 11);
+    createLayer(12211, 1221);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(2, 11);
+    reparentLayer(111, 12);
+    reparentLayer(1221, 1);
+    reparentLayer(1221, 13);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(1221, 13);
+    reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {2, 11, 111, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+    destroyLayerHandle(2);
+    destroyLayerHandle(11);
+    destroyLayerHandle(1221);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    destroyLayerHandle(111);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    // handle is destroyed but layer is kept alive and reachable by parent
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // destroy parent layer and the child gets destroyed
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, 1);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(11, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    createLayer(112, 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111, 112};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 12, 121, 122,
+                                                   1221, 13, 14,  11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    createLayer(1111, 111);
+    createLayer(112, 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 1111, 112, 12,   121, 122,
+                                                   1221, 13, 14,  11,   111, 1111, 112, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(111, 12);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   ├── 122
+    // │   │   │   └── 1221
+    // │   │   └ - 111 (relative)
+    // │   ├── 13
+    // │   └── 14
+    // │       └ * 11 (mirroring)
+    // └── 2
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 12, 111, 121, 122,
+                                                   1221, 13, 14,  11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    // 111 is not reachable in the mirror
+    expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(1221, 12);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   ├── 122
+    // │   │   │   └── 1221
+    // │   │   └ - 1221 (relative)
+    // │   ├── 13
+    // │   └── 14
+    // │       └ * 12 (mirroring)
+    // └── 2
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1,  11, 111, 12,  121, 122,  1221, 1221,
+                                                   13, 14, 12,  121, 122, 1221, 1221, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    // relative layer 1221 is traversable in the mirrored hierarchy as well
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentRelativeLayer(11, 2);
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(2);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // remove relative parent so layer becomes onscreen again
+    reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(1221, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    // verify relz path
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // offscreen layer cannot be reached as a relative child
+    reparentLayer(12, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {12, 121, 122, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // layer when onscreen can be reached as a relative child again
+    reparentLayer(12, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    updateBackgroundColor(1, 0.5);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createLayer(11, 1);
+    reparentLayer(1, 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    std::vector<uint32_t> expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    reparentRelativeLayer(11, 2);
+    reparentRelativeLayer(2, 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    // fix loop
+    uint32_t invalidRelativeRoot;
+    bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+    EXPECT_TRUE(hasRelZLoop);
+    mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+    hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+    EXPECT_EQ(invalidRelativeRoot, 11u);
+    EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    createLayer(111, 11);
+    createLayer(21, 2);
+    createLayer(22, 2);
+    createLayer(221, 22);
+    reparentRelativeLayer(22, 111);
+    reparentRelativeLayer(11, 221);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    // fix loop
+    uint32_t invalidRelativeRoot;
+    bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+    EXPECT_TRUE(hasRelZLoop);
+    mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+    hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+    EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 2, 21};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111, 22, 221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(1, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    createRootLayer(1);
+    destroyLayerHandle(1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+    setZ(122, -1);
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    auto checkTraversalPathIdVisitor =
+            [](const LayerHierarchy& hierarchy,
+               const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+        EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+        return true;
+    };
+    hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+    hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 8757e63..a3b3c4c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -31,6 +31,9 @@
 #include "FpsOps.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include "libsurfaceflinger_unittest_main.h"
 
 using namespace std::chrono_literals;
 
@@ -45,44 +48,41 @@
 
 using mock::createDisplayMode;
 
-// Use a C style macro to keep the line numbers printed in gtest
-#define EXPECT_SCORED_FRAME_RATE(modePtr, fps, scored) \
-    EXPECT_EQ((FrameRateMode{(fps), (modePtr)}), (scored).frameRateMode)
-
 struct TestableRefreshRateSelector : RefreshRateSelector {
     using RefreshRateSelector::FrameRateRanking;
     using RefreshRateSelector::RefreshRateOrder;
 
     using RefreshRateSelector::RefreshRateSelector;
 
-    void setActiveModeId(DisplayModeId modeId) {
+    void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateSelector::setActiveModeId(modeId);
+        return RefreshRateSelector::setActiveMode(modeId, renderFrameRate);
     }
 
     const DisplayMode& getActiveMode() const {
-        ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateSelector::getActiveMode();
+        std::lock_guard lock(mLock);
+        return *RefreshRateSelector::getActiveModeLocked().modePtr;
     }
 
-    DisplayModePtr getMinSupportedRefreshRate() const {
+    ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return mMinRefreshRateModeIt->second;
+        return ftl::as_non_null(mMinRefreshRateModeIt->second);
     }
 
-    DisplayModePtr getMaxSupportedRefreshRate() const {
+    ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return mMaxRefreshRateModeIt->second;
+        return ftl::as_non_null(mMaxRefreshRateModeIt->second);
     }
 
-    DisplayModePtr getMinRefreshRateByPolicy() const {
+    ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const {
         std::lock_guard lock(mLock);
-        return getMinRefreshRateByPolicyLocked();
+        return ftl::as_non_null(getMinRefreshRateByPolicyLocked());
     }
 
-    DisplayModePtr getMaxRefreshRateByPolicy() const {
+    ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const {
         std::lock_guard lock(mLock);
-        return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+        return ftl::as_non_null(
+                getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()));
     }
 
     FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
@@ -112,8 +112,8 @@
         return std::make_pair(ranking, consideredSignals);
     }
 
-    DisplayModePtr getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
-                                        GlobalSignals signals = {}) const {
+    ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
+            const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
         return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
     }
 
@@ -154,25 +154,42 @@
     static constexpr DisplayModeId kModeId60Frac{10};
     static constexpr DisplayModeId kModeId35{11};
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
-    static inline const DisplayModePtr kMode90_4K =
-            createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
-    static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
-    static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
-    static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
-    static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
-    static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
-    static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
-    static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
-    static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
-    static inline const DisplayModePtr kMode35 = createDisplayMode(kModeId35, 35_Hz);
-    static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
-    static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
-    static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode60Frac =
+            ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_4K =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30Frac =
+            ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode35 =
+            ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode50 =
+            ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24 =
+            ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
+            ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
 
     // Test configurations.
     static inline const DisplayModes kModes_60 = makeModes(kMode60);
@@ -298,7 +315,7 @@
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     const auto minRate90 = selector.getMinRefreshRateByPolicy();
     const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -322,7 +339,7 @@
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     const auto minRate90 = selector.getMinRefreshRateByPolicy();
     const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -358,7 +375,7 @@
         EXPECT_EQ(mode.getId(), kModeId60);
     }
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     {
         const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId90);
@@ -1805,7 +1822,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1826,7 +1843,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Verify that if the active mode is in another group and there are no layers with
     // Seamlessness::SeamedAndSeamless, we should switch back to the default group.
@@ -1850,7 +1867,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
     // Seamlessness::OnlySeamless can't change the mode group.
@@ -1879,7 +1896,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
     // Seamlessness::Default can't change the mode group back to the group of the default
@@ -1912,7 +1929,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Layer with Seamlessness::Default can change the mode group if there's an
     // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
@@ -1953,7 +1970,7 @@
 
     EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 
-    selector.setActiveModeId(kModeId120);
+    selector.setActiveMode(kModeId120, 120_Hz);
     EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
 }
 
@@ -1984,7 +2001,7 @@
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
-    selector.setActiveModeId(kModeId30);
+    selector.setActiveMode(kModeId30, 30_Hz);
 
     EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
 }
@@ -2102,7 +2119,7 @@
     EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     {
         constexpr bool kTouchActive = false;
@@ -2142,7 +2159,7 @@
 
     struct Expectation {
         Fps fps;
-        DisplayModePtr mode;
+        ftl::NonNull<DisplayModePtr> mode;
     };
 
     const std::initializer_list<Expectation> knownFrameRatesExpectations = {
@@ -2185,33 +2202,39 @@
     explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
     if (GetParam() == Config::FrameRateOverride::Disabled) {
-        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz, selector.getBestScoredFrameRate(layers));
-        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz,
-                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
 
     } else {
-        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
-        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz,
-                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
     }
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
 
     if (GetParam() == Config::FrameRateOverride::Disabled) {
-        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
     } else {
-        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
     }
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
@@ -2295,6 +2318,10 @@
 
 // b/190578904
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+    if (g_noSlowTests) {
+        GTEST_SKIP();
+    }
+
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
@@ -2409,23 +2436,23 @@
     Fps displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId60);
+    selector.setActiveMode(kModeId60, 60_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId72);
+    selector.setActiveMode(kModeId72, 72_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId120);
+    selector.setActiveMode(kModeId120, 120_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
@@ -2808,7 +2835,7 @@
     constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
                                        /*render*/ k0_120Hz};
 
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
                                         /*render*/ k0_120Hz};
@@ -2819,7 +2846,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
 
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
@@ -2831,7 +2858,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
 
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
@@ -2843,7 +2870,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
@@ -2858,17 +2885,20 @@
     layer.name = "30Hz ExplicitDefault";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::ExplicitDefault;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 
     layer.name = "30Hz Heuristic";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::Heuristic;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 
     layer.name = "30Hz ExplicitExactOrMultiple";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
@@ -2897,7 +2927,7 @@
     EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     {
         constexpr bool kTouchActive = false;
         EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
@@ -2913,5 +2943,18 @@
     EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
 }
 
+TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity());
+
+    using namespace fps_approx_ops;
+    selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}});
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+    EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 8e333a3..3ee53c9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -58,22 +58,22 @@
     SchedulerTest();
 
     static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
-    static inline const DisplayModePtr kDisplay1Mode60 =
-            createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kDisplay1Mode120 =
-            createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
     static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
 
     static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
-    static inline const DisplayModePtr kDisplay2Mode60 =
-            createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kDisplay2Mode120 =
-            createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
     static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
 
     static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
-    static inline const DisplayModePtr kDisplay3Mode60 =
-            createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz));
     static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
 
     std::shared_ptr<RefreshRateSelector> mSelector =
@@ -217,7 +217,9 @@
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
     ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+    EXPECT_NO_FATAL_FAILURE(
+            mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+                                                       {90_Hz, ftl::as_non_null(mode)}));
     EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
@@ -232,7 +234,7 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg.front().modePtr->getFps(), 120_Hz);
+    return isApproxEqual(arg.front().mode.fps, 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
@@ -277,7 +279,7 @@
 
     auto choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode60, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals));
 
     globalSignals = {.idle = false};
     mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -287,7 +289,7 @@
 
     choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
 
     globalSignals = {.touch = true};
     mScheduler->replaceTouchTimer(10);
@@ -298,7 +300,7 @@
 
     choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
 
     mScheduler->unregisterDisplay(kDisplayId1);
     EXPECT_FALSE(mScheduler->hasRefreshRateSelectors());
@@ -319,8 +321,11 @@
         const GlobalSignals globalSignals = {.idle = true};
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode60,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{60_Hz, kDisplay1Mode60},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay2Mode60},
                                                                  globalSignals);
 
         std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
@@ -335,8 +340,11 @@
         const GlobalSignals globalSignals = {.idle = false};
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode120,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
                                                                  globalSignals);
 
         mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -351,8 +359,11 @@
 
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode120,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
                                                                  globalSignals);
 
         const auto actualChoices = mScheduler->chooseDisplayModes();
@@ -369,13 +380,15 @@
         mScheduler->replaceTouchTimer(10);
         mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
 
-        expectedChoices =
-                ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode60,
-                                                                 globalSignals)(kDisplayId3,
-                                                                                kDisplay3Mode60,
-                                                                                globalSignals);
+        expectedChoices = ftl::init::map<
+                const PhysicalDisplayId&,
+                DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
+                                   globalSignals)(kDisplayId2,
+                                                  FrameRateMode{60_Hz, kDisplay2Mode60},
+                                                  globalSignals)(kDisplayId3,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay3Mode60},
+                                                                 globalSignals);
 
         const auto actualChoices = mScheduler->chooseDisplayModes();
         EXPECT_EQ(expectedChoices, actualChoices);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 05d0ebf..074bf8c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -116,7 +116,7 @@
     ftl::FakeGuard guard(kMainThreadContext);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -125,8 +125,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -139,17 +139,18 @@
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+    EXPECT_CALL(*mAppEventThread,
+                onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
     mFlinger.commit();
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
@@ -164,8 +165,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -175,12 +176,13 @@
                                                hal::HWConfigId(kModeId90.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+    EXPECT_CALL(*mAppEventThread,
+                onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -190,7 +192,7 @@
     // is still being processed the later call will be respected.
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -211,7 +213,7 @@
                                                                      180));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -221,19 +223,19 @@
     mFlinger.commit();
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
     ftl::FakeGuard guard(kMainThreadContext);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -242,8 +244,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -278,7 +280,7 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+    void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+        DisplayModes modes;
+        for (DisplayModePtr displayMode : displayModePtrs) {
+            modes.try_emplace(displayMode->getId(), displayMode);
+        }
+
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+                           .inject();
+        mDisplay->overrideHdrTypes(types);
+    }
+
+protected:
+    sp<DisplayDevice> mDisplay;
+
+    static constexpr DisplayModeId modeId1080p60{0};
+    static constexpr DisplayModeId modeId4k30{1};
+    static constexpr DisplayModeId modeId4k60{2};
+
+    static inline const DisplayModePtr mode1080p60 =
+            createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+    static inline const DisplayModePtr mode4k30 =
+            createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+    static inline const DisplayModePtr mode4k30NonStandard =
+            createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+    static inline const DisplayModePtr mode4k60 =
+            createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+    const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+                                        ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+    injectDisplayModes({mode4k60});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+    ASSERT_EQ(1, displayModes.size());
+    ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+                            displayModes[0].supportedHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+    ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+    injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+    ASSERT_EQ(2, displayModes.size());
+    for (size_t i = 0; i < displayModes.size(); i++) {
+        ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+                                displayModes[i].supportedHdrTypes.end(),
+                                [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+        ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+                                displayModes[i].supportedHdrTypes.end(),
+                                [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+        ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+    }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+    injectDisplayModes({mode4k60});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+    ASSERT_EQ(2, displayHdrTypes.size());
+    ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+    ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index bc66961..622717f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,6 @@
                     .setNativeWindow(mNativeWindow)
                     .setPowerMode(hal::PowerMode::ON)
                     .inject();
-    mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
 }
 
 void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 25857ec..0fb8e2b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -259,12 +259,6 @@
         auto injector = Display::makeFakeExistingDisplayInjector(test);
         const auto display = injector.inject();
         display->setPowerMode(mode);
-        if (injector.physicalDisplay()
-                    .transform(&display::PhysicalDisplay::isInternal)
-                    .value_or(false)) {
-            test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
-        }
-
         return display;
     }
 
@@ -490,5 +484,37 @@
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
+TEST_F(SetPowerModeInternalTest, designatesLeaderDisplay) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // Inject a primary display with uninitialized power mode.
+    constexpr bool kInitPowerMode = false;
+    Case::Display::injectHwcDisplay<kInitPowerMode>(this);
+    auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+    injector.setPowerMode(std::nullopt);
+    const auto display = injector.inject();
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
+    // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
+    // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
+    constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
+    ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
+    mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
+
+    mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The primary display should be designated as the leader.
+    EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 0384568..c0796df 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -288,7 +288,7 @@
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
         ftl::FakeGuard guard(kMainThreadContext);
-        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
+        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f8fe0d..b8a6063 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,8 +44,7 @@
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
 
-        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
-                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
@@ -88,6 +87,10 @@
         Scheduler::unregisterDisplay(displayId);
     }
 
+    std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+        return mLeaderDisplayId;
+    }
+
     void setLeaderDisplay(PhysicalDisplayId displayId) {
         ftl::FakeGuard guard(kMainThreadContext);
         Scheduler::setLeaderDisplay(displayId);
@@ -147,7 +150,7 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
         Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c15b3c8..2117084 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,6 +30,7 @@
 #include <ftl/fake_guard.h>
 #include <gui/ScreenCaptureResults.h>
 
+#include <ui/DynamicDisplayInfo.h>
 #include "DisplayDevice.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
@@ -221,7 +222,7 @@
             selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
         }
 
-        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps());
+        const auto fps = selectorPtr->getActiveMode().fps;
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -470,7 +471,7 @@
     void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
         ftl::FakeGuard guard(kMainThreadContext);
-        mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+        mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
     }
 
     auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
@@ -486,6 +487,11 @@
 
     void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
 
+    void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                        ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+        mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -621,7 +627,7 @@
             return *this;
         }
 
-        auto& setPowerMode(hal::PowerMode mode) {
+        auto& setPowerMode(std::optional<hal::PowerMode> mode) {
             mPowerMode = mode;
             return *this;
         }
@@ -643,16 +649,18 @@
             // is much longer lived.
             auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
                                                          mHwcDisplayType);
-
             display->mutableIsConnected() = true;
-            display->setPowerMode(mPowerMode);
+
+            if (mPowerMode) {
+                display->setPowerMode(*mPowerMode);
+            }
+
             flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
                     .WillRepeatedly(
                             DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
                                   Return(hal::Error::NONE)));
-
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
                     .WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -711,7 +719,7 @@
         int32_t mDpiY = DEFAULT_DPI;
         int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
         hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
-        hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+        std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
         const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
                 mCapabilities = nullptr;
     };
@@ -788,7 +796,7 @@
             return *this;
         }
 
-        auto& setPowerMode(hal::PowerMode mode) {
+        auto& setPowerMode(std::optional<hal::PowerMode> mode) {
             mCreationArgs.initialPowerMode = mode;
             return *this;
         }
@@ -853,27 +861,32 @@
                 LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
                 const auto physicalId = *physicalIdOpt;
 
+                if (mCreationArgs.isPrimary) {
+                    mFlinger.mutableActiveDisplayId() = physicalId;
+                }
+
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
+                const auto fps = activeMode->get()->getFps();
 
                 state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .activeMode = activeMode->get()};
 
-                const auto it = mFlinger.mutablePhysicalDisplays()
-                                        .emplace_or_replace(physicalId, mDisplayToken, physicalId,
-                                                            *mConnectionType, std::move(modes),
-                                                            ui::ColorModes(), std::nullopt)
-                                        .first;
+                mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
+                                                                      physicalId, *mConnectionType,
+                                                                      std::move(modes),
+                                                                      ui::ColorModes(),
+                                                                      std::nullopt);
 
                 if (mFlinger.scheduler()) {
                     mFlinger.scheduler()->registerDisplay(physicalId,
                                                           display->holdRefreshRateSelector());
                 }
 
-                display->setActiveMode(activeModeId, it->second.snapshot());
+                display->setActiveMode(activeModeId, fps, fps);
             }
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 1dd4f25..0aaefe3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1099,8 +1099,10 @@
                                       kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
                                       DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1234,8 +1236,10 @@
                                       GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
                                       DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1322,8 +1326,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1412,8 +1418,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1437,8 +1445,10 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1456,8 +1466,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1476,8 +1488,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2da266b..47c2dee 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setDivisor(unsigned) final {}
     void dump(std::string&) const final {}
 
 private:
@@ -91,6 +92,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setDivisor(unsigned) final {}
     void dump(std::string&) const final {}
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f660753..2b86e94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -55,6 +55,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 74d2b7d..3095e8a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -532,6 +532,26 @@
     EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
 }
 
+TEST_F(VSyncPredictorTest, setDivisorIsRespected) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    tracker.setDivisor(3);
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a35ff96..8bd689a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,6 +50,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
index c2c3d77..5654691 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -36,7 +36,7 @@
     MockAidlPowerHalWrapper();
     ~MockAidlPowerHalWrapper() override;
     MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
-    MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, notifyDisplayUpdateImminentAndCpuReset, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
     MOCK_METHOD(void, restartPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5ee38ec..836e3a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
 using android::hardware::graphics::composer::V2_1::Config;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fb1b394..7fc625c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,7 +32,7 @@
     MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
                 (override));
     MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ded6806..f8567bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,7 +34,7 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
+    MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
     MOCK_METHOD2(onFrameRateOverridesChanged,
                  void(PhysicalDisplayId, std::vector<FrameRateOverride>));
     MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
similarity index 65%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index fc2542b..ef9cd9b 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2022 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.
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-parcelable GenericDataParcelable {
-    int data;
-    float majorVersion;
-    float minorVersion;
-    IBinder binder;
-    ParcelFileDescriptor fileDescriptor;
-    int[] array;
-}
\ No newline at end of file
+#pragma once
+
+#include <scheduler/FrameRateMode.h>
+
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
+    EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
     TimeStats();
     ~TimeStats() override;
 
-    MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+    MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
     MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
     MOCK_METHOD0(isEnabled, bool());
     MOCK_METHOD0(miniDump, std::string());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 5b0c1f3..6893154 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,6 +34,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
deleted file mode 100644
index 80e9a3c..0000000
--- a/services/vr/hardware_composer/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_shared {
-    name: "libvr_hwc-hal",
-
-    system_ext_specific: true,
-
-    srcs: [
-        "impl/vr_hwc.cpp",
-        "impl/vr_composer_client.cpp",
-    ],
-
-    static_libs: [
-        "libbroadcastring",
-        "libdisplay",
-    ],
-
-    shared_libs: [
-        "android.frameworks.vr.composer@2.0",
-        "android.hardware.graphics.composer@2.1",
-        "android.hardware.graphics.composer@2.2",
-        "android.hardware.graphics.composer@2.3",
-        "android.hardware.graphics.composer@2.1-resources",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
-        "libbase",
-        "libbufferhubqueue",
-        "libbinder",
-        "libcutils",
-        "libfmq",
-        "libhardware",
-        "libhidlbase",
-        "liblog",
-        "libsync",
-        "libui",
-        "libutils",
-        "libpdx_default_transport",
-    ],
-
-    header_libs: [
-        "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.3-hal",
-    ],
-
-    export_header_lib_headers: [
-        "android.hardware.graphics.composer@2.3-hal",
-    ],
-
-    export_static_lib_headers: [
-        "libdisplay",
-    ],
-
-    export_shared_lib_headers: [
-        "android.frameworks.vr.composer@2.0",
-        "android.hardware.graphics.composer@2.1",
-        "android.hardware.graphics.composer@2.2",
-        "android.hardware.graphics.composer@2.3",
-    ],
-
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-DLOG_TAG=\"vr_hwc\"",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-Wall",
-        "-Werror",
-        "-Wno-error=unused-private-field",
-        // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
-        "-Wno-sign-compare",
-        "-Wno-unused-parameter",
-    ],
-
-}
-
-cc_library_static {
-    name: "libvr_hwc-impl",
-    srcs: [
-        "vr_composer.cpp",
-    ],
-    static_libs: [
-        "libvr_hwc-binder",
-    ],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libui",
-        "libutils",
-        "libvr_hwc-hal",
-    ],
-    export_shared_lib_headers: [
-        "libvr_hwc-hal",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"vr_hwc\"",
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_test {
-    name: "vr_hwc_test",
-    gtest: true,
-    srcs: ["tests/vr_composer_test.cpp"],
-    static_libs: [
-        "libgtest",
-        "libvr_hwc-impl",
-        // NOTE: This needs to be included after the *-impl lib otherwise the
-        // symbols in the *-binder library get optimized out.
-        "libvr_hwc-binder",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
-        "-Wno-sign-compare",
-        "-Wno-unused-parameter",
-    ],
-    shared_libs: [
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libui",
-        "libutils",
-    ],
-}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
deleted file mode 100644
index fa71ed7..0000000
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_static {
-    name: "libvr_hwc-binder",
-    srcs: [
-        "android/dvr/IVrComposer.aidl",
-        "android/dvr/IVrComposerCallback.aidl",
-        "android/dvr/parcelable_composer_frame.cpp",
-        "android/dvr/parcelable_composer_layer.cpp",
-        "android/dvr/parcelable_unique_fd.cpp",
-    ],
-    aidl: {
-        local_include_dirs: ["."],
-        export_aidl_headers: true,
-    },
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    shared_libs: [
-        "libbinder",
-        "libui",
-        "libutils",
-        "libvr_hwc-hal",
-    ],
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
deleted file mode 100644
index be1ec5b..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-package android.dvr;
-
-import android.dvr.IVrComposerCallback;
-
-/**
- * Service interface exposed by VR HWC exposed to system apps which allows one
- * system app to connect to get SurfaceFlinger's outputs (all displays). This
- * is active when SurfaceFlinger is in VR mode, where all 2D output is
- * redirected to VR HWC.
- *
- * @hide */
-interface IVrComposer
-{
-  const String SERVICE_NAME = "vr_hwc";
-
-  /**
-   * Registers a callback used to receive frame notifications.
-   */
-  void registerObserver(in IVrComposerCallback callback);
-
-  /**
-   * Clears a previously registered frame notification callback.
-   */
-  void clearObserver();
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
deleted file mode 100644
index aa70de1..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-package android.dvr;
-
-import android.dvr.ParcelableComposerFrame;
-import android.dvr.ParcelableUniqueFd;
-
-/**
- * A system app will implement and register this callback with VRComposer
- * to receive the layers SurfaceFlinger presented when in VR mode.
- *
- * @hide */
-interface IVrComposerCallback {
-  /**
-   * Called by the VR HWC service when a new frame is ready to be presented.
-   *
-   * @param frame The new frame VR HWC wants to present.
-   * @return A fence FD used to signal when the previous frame is no longer
-   * used by the client. This may be an invalid fence (-1) if the client is not
-   * using the previous frame, in which case the previous frame may be re-used
-   * at any point in time.
-   */
-  ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame);
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
deleted file mode 100644
index 84abc19..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
deleted file mode 100644
index a200345..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
deleted file mode 100644
index eee9d13..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
deleted file mode 100644
index db7d5dc..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_frame.h"
-
-#include <binder/Parcel.h>
-
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerFrame::ParcelableComposerFrame() {}
-
-ParcelableComposerFrame::ParcelableComposerFrame(
-    const ComposerView::Frame& frame)
-    : frame_(frame) {}
-
-ParcelableComposerFrame::~ParcelableComposerFrame() {}
-
-status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeUint64(frame_.display_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.display_width);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.display_height);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeBool(frame_.removed);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(frame_.color_transform_hint);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < 16; i++) {
-    ret = parcel->writeFloat(frame_.color_transform[i]);
-    if (ret != OK) return ret;
-  }
-
-  std::vector<ParcelableComposerLayer> layers;
-  for (size_t i = 0; i < frame_.layers.size(); ++i)
-    layers.push_back(ParcelableComposerLayer(frame_.layers[i]));
-
-  ret = parcel->writeParcelableVector(layers);
-
-  return ret;
-}
-
-status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) {
-  status_t ret = parcel->readUint64(&frame_.display_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&frame_.display_width);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&frame_.display_height);
-  if (ret != OK) return ret;
-
-  ret = parcel->readBool(&frame_.removed);
-  if (ret != OK) return ret;
-
-  uint32_t value;
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.active_config = static_cast<Config>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.color_mode = static_cast<ColorMode>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.power_mode = static_cast<IComposerClient::PowerMode>(value);
-
-  ret = parcel->readUint32(&value);
-  if (ret != OK) return ret;
-  frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value);
-
-  ret = parcel->readInt32(&frame_.color_transform_hint);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < 16; i++) {
-    ret = parcel->readFloat(&frame_.color_transform[i]);
-    if (ret != OK) return ret;
-  }
-
-  std::vector<ParcelableComposerLayer> layers;
-  ret = parcel->readParcelableVector(&layers);
-  if (ret != OK) return ret;
-
-  frame_.layers.clear();
-  for (size_t i = 0; i < layers.size(); ++i)
-    frame_.layers.push_back(layers[i].layer());
-
-  return ret;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
deleted file mode 100644
index a82df7f..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerFrame : public Parcelable {
- public:
-  ParcelableComposerFrame();
-  explicit ParcelableComposerFrame(const ComposerView::Frame& frame);
-  ~ParcelableComposerFrame() override;
-
-  ComposerView::Frame frame() const { return frame_; }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  ComposerView::Frame frame_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
deleted file mode 100644
index c3621eb..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-#include <binder/Parcel.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicBufferMapper.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerLayer::ParcelableComposerLayer() {}
-
-ParcelableComposerLayer::ParcelableComposerLayer(
-    const ComposerView::ComposerLayer& layer) : layer_(layer) {}
-
-ParcelableComposerLayer::~ParcelableComposerLayer() {}
-
-status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeUint64(layer_.id);
-  if (ret != OK) return ret;
-
-  ret = parcel->write(*layer_.buffer);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeBool(layer_.fence->isValid());
-  if (ret != OK) return ret;
-
-  if (layer_.fence->isValid()) {
-    ret = parcel->writeFileDescriptor(layer_.fence->dup(), true);
-    if (ret != OK) return ret;
-  }
-
-  ret = parcel->writeInt32(layer_.display_frame.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.display_frame.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.crop.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->writeFloat(layer_.alpha);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.type);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.app_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(layer_.z_order);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.cursor_x);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.cursor_y);
-  if (ret != OK) return ret;
-
-  uint32_t color = layer_.color.r |
-      (static_cast<uint32_t>(layer_.color.g) << 8) |
-      (static_cast<uint32_t>(layer_.color.b) << 16) |
-      (static_cast<uint32_t>(layer_.color.a) << 24);
-  ret = parcel->writeUint32(color);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.dataspace);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeInt32(layer_.transform);
-  if (ret != OK) return ret;
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size()));
-  if (ret != OK) return ret;
-
-  for (auto& rect: layer_.visible_regions) {
-    ret = parcel->writeInt32(rect.left);
-    ret = parcel->writeInt32(rect.top);
-    ret = parcel->writeInt32(rect.right);
-    ret = parcel->writeInt32(rect.bottom);
-    if (ret != OK) return ret;
-  }
-
-  ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size()));
-  if (ret != OK) return ret;
-
-  for (auto& rect: layer_.damaged_regions) {
-    ret = parcel->writeInt32(rect.left);
-    ret = parcel->writeInt32(rect.top);
-    ret = parcel->writeInt32(rect.right);
-    ret = parcel->writeInt32(rect.bottom);
-    if (ret != OK) return ret;
-  }
-
-  return OK;
-}
-
-status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) {
-  status_t ret = parcel->readUint64(&layer_.id);
-  if (ret != OK) return ret;
-
-  layer_.buffer = new GraphicBuffer();
-  ret = parcel->read(*layer_.buffer);
-  if (ret != OK) {
-    layer_.buffer.clear();
-    return ret;
-  }
-
-  bool has_fence = 0;
-  ret = parcel->readBool(&has_fence);
-  if (ret != OK) return ret;
-
-  if (has_fence)
-    layer_.fence = new Fence(dup(parcel->readFileDescriptor()));
-  else
-    layer_.fence = new Fence();
-
-  ret = parcel->readInt32(&layer_.display_frame.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.display_frame.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.left);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.top);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.right);
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.crop.bottom);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode));
-  if (ret != OK) return ret;
-
-  ret = parcel->readFloat(&layer_.alpha);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.type);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.app_id);
-  if (ret != OK) return ret;
-
-  ret = parcel->readUint32(&layer_.z_order);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.cursor_x);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.cursor_y);
-  if (ret != OK) return ret;
-
-  uint32_t color;
-  ret = parcel->readUint32(&color);
-  if (ret != OK) return ret;
-  layer_.color.r = color & 0xFF;
-  layer_.color.g = (color >> 8) & 0xFF;
-  layer_.color.b = (color >> 16) & 0xFF;
-  layer_.color.a = (color >> 24) & 0xFF;
-
-  ret = parcel->readInt32(&layer_.dataspace);
-  if (ret != OK) return ret;
-
-  ret = parcel->readInt32(&layer_.transform);
-  if (ret != OK) return ret;
-
-  uint32_t size;
-  ret = parcel->readUint32(&size);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < size; i++) {
-    hwc_rect_t rect;
-    ret = parcel->readInt32(&rect.left);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.top);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.right);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.bottom);
-    if (ret != OK) return ret;
-
-    layer_.visible_regions.push_back(rect);
-  }
-
-  ret = parcel->readUint32(&size);
-  if (ret != OK) return ret;
-
-  for(size_t i = 0; i < size; i++) {
-    hwc_rect_t rect;
-    ret = parcel->readInt32(&rect.left);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.top);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.right);
-    if (ret != OK) return ret;
-
-    ret = parcel->readInt32(&rect.bottom);
-    if (ret != OK) return ret;
-
-    layer_.damaged_regions.push_back(rect);
-  }
-
-  return OK;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
deleted file mode 100644
index 6d2ac09..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerLayer : public Parcelable {
- public:
-  ParcelableComposerLayer();
-  explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer);
-  ~ParcelableComposerLayer() override;
-
-  ComposerView::ComposerLayer layer() const { return layer_; }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  ComposerView::ComposerLayer layer_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
deleted file mode 100644
index 9486f3c..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "android/dvr/parcelable_unique_fd.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableUniqueFd::ParcelableUniqueFd() {}
-
-ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence)
-    : fence_(dup(fence.get())) {}
-
-ParcelableUniqueFd::~ParcelableUniqueFd() {}
-
-status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const {
-  status_t ret = parcel->writeBool(fence_.get() >= 0);
-  if (ret != OK) return ret;
-
-  if (fence_.get() >= 0)
-    ret = parcel->writeUniqueFileDescriptor(fence_);
-
-  return ret;
-}
-
-status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) {
-  bool has_fence = 0;
-  status_t ret = parcel->readBool(&has_fence);
-  if (ret != OK) return ret;
-
-  if (has_fence)
-    ret = parcel->readUniqueFileDescriptor(&fence_);
-
-  return ret;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
deleted file mode 100644
index c4216f6..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-
-#include <android-base/unique_fd.h>
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace dvr {
-
-// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the
-// case where the FD is invalid (-1), unlike FileDescriptor which expects a
-// valid FD.
-class ParcelableUniqueFd : public Parcelable {
- public:
-  ParcelableUniqueFd();
-  explicit ParcelableUniqueFd(const base::unique_fd& fence);
-  ~ParcelableUniqueFd() override;
-
-  void set_fence(const base::unique_fd& fence) {
-    fence_.reset(dup(fence.get()));
-  }
-  base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); }
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  base::unique_fd fence_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
deleted file mode 100644
index dd1603d..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2016 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 <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <hardware/gralloc.h>
-#include <hardware/gralloc1.h>
-#include <log/log.h>
-
-#include <memory>
-
-#include "impl/vr_hwc.h"
-#include "impl/vr_composer_client.h"
-
-namespace android {
-namespace dvr {
-
-using android::frameworks::vr::composer::V2_0::IVrComposerClient;
-
-VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
-    : ComposerClient(&hal), mVrHal(hal) {
-  if (!init()) {
-      LOG_ALWAYS_FATAL("failed to initialize VrComposerClient");
-  }
-}
-
-VrComposerClient::~VrComposerClient() {}
-
-std::unique_ptr<ComposerCommandEngine>
-VrComposerClient::createCommandEngine() {
-  return std::make_unique<VrCommandEngine>(*this);
-}
-
-VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
-    : ComposerCommandEngine(client.mHal, client.mResources.get()),
-      mVrHal(client.mVrHal) {}
-
-VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
-
-bool VrComposerClient::VrCommandEngine::executeCommand(
-    hardware::graphics::composer::V2_1::IComposerClient::Command command,
-    uint16_t length) {
-  IVrComposerClient::VrCommand vrCommand =
-      static_cast<IVrComposerClient::VrCommand>(command);
-  switch (vrCommand) {
-    case IVrComposerClient::VrCommand::SET_LAYER_INFO:
-      return executeSetLayerInfo(length);
-    case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA:
-      return executeSetClientTargetMetadata(length);
-    case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA:
-      return executeSetLayerBufferMetadata(length);
-    default:
-      return ComposerCommandEngine::executeCommand(command, length);
-  }
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) {
-  if (length != 2) {
-    return false;
-  }
-
-  auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
-  if (err != Error::NONE) {
-    mWriter->setError(getCommandLoc(), err);
-  }
-
-  return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata(
-    uint16_t length) {
-  if (length != 7)
-    return false;
-
-  auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
-  if (err != Error::NONE)
-    mWriter->setError(getCommandLoc(), err);
-
-  return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata(
-    uint16_t length) {
-  if (length != 7)
-    return false;
-
-  auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
-                                           readBufferMetadata());
-  if (err != Error::NONE)
-    mWriter->setError(getCommandLoc(), err);
-
-  return true;
-}
-
-IVrComposerClient::BufferMetadata
-VrComposerClient::VrCommandEngine::readBufferMetadata() {
-  IVrComposerClient::BufferMetadata metadata = {
-      .width = read(),
-      .height = read(),
-      .stride = read(),
-      .layerCount = read(),
-      .format =
-          static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
-              readSigned()),
-      .usage = read64(),
-  };
-  return metadata;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
deleted file mode 100644
index 1b2b5f4..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2017 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.
- */
-
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.1/ComposerCommandEngine.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-
-namespace android {
-namespace dvr {
-
-class VrHwc;
-
-using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_3::hal::ComposerHal;
-using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
-
-using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
-
-class VrComposerClient : public ComposerClient {
- public:
-  explicit VrComposerClient(android::dvr::VrHwc& hal);
-  virtual ~VrComposerClient();
-
- private:
-  class VrCommandEngine : public ComposerCommandEngine {
-   public:
-    explicit VrCommandEngine(VrComposerClient& client);
-    ~VrCommandEngine() override;
-
-    bool executeCommand(
-        hardware::graphics::composer::V2_1::IComposerClient::Command command,
-        uint16_t length) override;
-
-   private:
-    bool executeSetLayerInfo(uint16_t length);
-    bool executeSetClientTargetMetadata(uint16_t length);
-    bool executeSetLayerBufferMetadata(uint16_t length);
-
-    IVrComposerClient::BufferMetadata readBufferMetadata();
-
-    android::dvr::VrHwc& mVrHal;
-
-    VrCommandEngine(const VrCommandEngine&) = delete;
-    void operator=(const VrCommandEngine&) = delete;
-  };
-
-  VrComposerClient(const VrComposerClient&) = delete;
-  void operator=(const VrComposerClient&) = delete;
-
-  std::unique_ptr<ComposerCommandEngine> createCommandEngine() override;
-  dvr::VrHwc& mVrHal;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
deleted file mode 100644
index e530b16..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * Copyright 2016 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 "impl/vr_hwc.h"
-
-#include "android-base/stringprintf.h"
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <private/dvr/display_client.h>
-#include <ui/Fence.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include "vr_composer_client.h"
-
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::base::StringPrintf;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-namespace types = android::hardware::graphics::common;
-
-namespace android {
-namespace dvr {
-namespace {
-
-const Display kDefaultDisplayId = 1;
-const Config kDefaultConfigId = 1;
-
-sp<GraphicBuffer> CreateGraphicBuffer(
-    const native_handle_t* handle,
-    const IVrComposerClient::BufferMetadata& metadata) {
-   sp<GraphicBuffer> buffer = new GraphicBuffer(
-      handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height,
-      static_cast<int32_t>(metadata.format), metadata.layerCount,
-      metadata.usage, metadata.stride);
-   if (buffer->initCheck() != OK) {
-     ALOGE("Failed to create graphic buffer");
-     return nullptr;
-   }
-
-   return buffer;
-}
-
-void GetPrimaryDisplaySize(int32_t* width, int32_t* height) {
-  *width = 1080;
-  *height = 1920;
-
-  int error = 0;
-  auto display_client = display::DisplayClient::Create(&error);
-  if (!display_client) {
-    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
-          error);
-    return;
-  }
-
-  auto status = display_client->GetDisplayMetrics();
-  if (!status) {
-    ALOGE("Could not get display metrics from display service : %s(%d)",
-          status.GetErrorMessage().c_str(), status.error());
-    return;
-  }
-
-  *width = status.get().display_width;
-  *height = status.get().display_height;
-}
-
-}  // namespace
-
-HwcDisplay::HwcDisplay(int32_t width, int32_t height)
-    : width_(width), height_(height) {}
-
-HwcDisplay::~HwcDisplay() {}
-
-bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
-                                 base::unique_fd fence) {
-  if (handle)
-    buffer_ = CreateGraphicBuffer(handle, buffer_metadata_);
-
-  fence_ = new Fence(fence.release());
-  return true;
-}
-
-void HwcDisplay::SetClientTargetMetadata(
-    const IVrComposerClient::BufferMetadata& metadata) {
-  buffer_metadata_ = metadata;
-}
-
-HwcLayer* HwcDisplay::CreateLayer() {
-  uint64_t layer_id = layer_ids_++;
-  layers_.push_back(HwcLayer(layer_id));
-  return &layers_.back();
-}
-
-HwcLayer* HwcDisplay::GetLayer(Layer id) {
-  for (size_t i = 0; i < layers_.size(); ++i)
-    if (layers_[i].info.id == id)
-      return &layers_[i];
-
-  return nullptr;
-}
-
-bool HwcDisplay::DestroyLayer(Layer id) {
-  for (auto it = layers_.begin(); it != layers_.end(); ++it) {
-    if (it->info.id == id) {
-      layers_.erase(it);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void HwcDisplay::GetChangedCompositionTypes(
-    std::vector<Layer>* layer_ids,
-    std::vector<IComposerClient::Composition>* types) {
-  std::sort(layers_.begin(), layers_.end(),
-            [](const auto& lhs, const auto& rhs) {
-              return lhs.info.z_order < rhs.info.z_order;
-            });
-
-  const size_t no_layer = std::numeric_limits<size_t>::max();
-  size_t first_client_layer = no_layer, last_client_layer = no_layer;
-  for (size_t i = 0; i < layers_.size(); ++i) {
-    switch (layers_[i].composition_type) {
-      case IComposerClient::Composition::SOLID_COLOR:
-      case IComposerClient::Composition::CURSOR:
-      case IComposerClient::Composition::SIDEBAND:
-        if (first_client_layer == no_layer)
-          first_client_layer = i;
-
-        last_client_layer = i;
-        break;
-      default:
-        break;
-    }
-  }
-
-  for (size_t i = 0; i < layers_.size(); ++i) {
-    if (i >= first_client_layer && i <= last_client_layer) {
-      if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
-        layer_ids->push_back(layers_[i].info.id);
-        types->push_back(IComposerClient::Composition::CLIENT);
-        layers_[i].composition_type = IComposerClient::Composition::CLIENT;
-      }
-
-      continue;
-    }
-
-    if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
-      layer_ids->push_back(layers_[i].info.id);
-      types->push_back(IComposerClient::Composition::DEVICE);
-      layers_[i].composition_type = IComposerClient::Composition::DEVICE;
-    }
-  }
-}
-
-Error HwcDisplay::GetFrame(
-    std::vector<ComposerView::ComposerLayer>* out_frames) {
-  bool queued_client_target = false;
-  std::vector<ComposerView::ComposerLayer> frame;
-  for (const auto& layer : layers_) {
-    if (layer.composition_type == IComposerClient::Composition::CLIENT) {
-      if (queued_client_target)
-        continue;
-
-      if (!buffer_.get()) {
-        ALOGE("Client composition requested but no client target buffer");
-        return Error::BAD_LAYER;
-      }
-
-      ComposerView::ComposerLayer client_target_layer = {
-          .buffer = buffer_,
-          .fence = fence_.get() ? fence_ : new Fence(-1),
-          .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
-            static_cast<int32_t>(buffer_->getHeight())},
-          .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
-            static_cast<float>(buffer_->getHeight())},
-          .blend_mode = IComposerClient::BlendMode::NONE,
-      };
-
-      frame.push_back(client_target_layer);
-      queued_client_target = true;
-    } else {
-      if (!layer.info.buffer.get() || !layer.info.fence.get()) {
-        ALOGV("Layer requested without valid buffer");
-        continue;
-      }
-
-      frame.push_back(layer.info);
-    }
-  }
-
-  out_frames->swap(frame);
-  return Error::NONE;
-}
-
-std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
-  std::vector<Layer> last_frame_layers;
-  last_frame_layers.swap(last_frame_layers_ids_);
-
-  for (const auto& layer : layers_)
-    last_frame_layers_ids_.push_back(layer.info.id);
-
-  return last_frame_layers;
-}
-
-void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) {
-  color_transform_hint_ = hint;
-  if (matrix)
-    memcpy(color_transform_, matrix, sizeof(color_transform_));
-}
-
-void HwcDisplay::dumpDebugInfo(std::string* result) const {
-  if (!result) {
-    return;
-  }
-  *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\
-      , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_);
-  *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\
-      layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height,
-      buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format);
-  for (const auto& layer : layers_) {
-    layer.dumpDebugInfo(result);
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// VrHwcClient
-
-VrHwc::VrHwc() {
-  vsync_callback_ = new VsyncCallback;
-}
-
-VrHwc::~VrHwc() {
-  vsync_callback_->SetEventCallback(nullptr);
-}
-
-bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
-
-void VrHwc::registerEventCallback(EventCallback* callback) {
-  std::unique_lock<std::mutex> lock(mutex_);
-  event_callback_ = callback;
-  int32_t width, height;
-  GetPrimaryDisplaySize(&width, &height);
-  // Create the primary display late to avoid initialization issues between
-  // VR HWC and SurfaceFlinger.
-  displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-
-  // Surface flinger will make calls back into vr_hwc when it receives the
-  // onHotplug() call, so it's important to release mutex_ here.
-  lock.unlock();
-  event_callback_->onHotplug(kDefaultDisplayId,
-                             hardware::graphics::composer::V2_1::
-                                 IComposerCallback::Connection::CONNECTED);
-  lock.lock();
-  UpdateVsyncCallbackEnabledLocked();
-}
-
-void VrHwc::unregisterEventCallback() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  event_callback_ = nullptr;
-  UpdateVsyncCallbackEnabledLocked();
-}
-
-uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
-
-Error VrHwc::destroyVirtualDisplay(Display display) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (display == kDefaultDisplayId || displays_.erase(display) == 0)
-    return Error::BAD_DISPLAY;
-  ComposerView::Frame frame;
-  frame.display_id = display;
-  frame.removed = true;
-  if (observer_)
-    observer_->OnNewFrame(frame);
-  return Error::NONE;
-}
-
-Error VrHwc::createLayer(Display display, Layer* outLayer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* layer = display_ptr->CreateLayer();
-  *outLayer = layer->info.id;
-  return Error::NONE;
-}
-
-Error VrHwc::destroyLayer(Display display, Layer layer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    return Error::BAD_DISPLAY;
-  }
-
-  return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
-}
-
-Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  *outConfig = kDefaultConfigId;
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayAttribute(Display display, Config config,
-                                 IComposerClient::Attribute attribute,
-                                 int32_t* outValue) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    return Error::BAD_DISPLAY;
-  }
-  if (config != kDefaultConfigId) {
-    return Error::BAD_CONFIG;
-  }
-
-  switch (attribute) {
-    case IComposerClient::Attribute::WIDTH:
-      *outValue = display_ptr->width();
-      break;
-    case IComposerClient::Attribute::HEIGHT:
-      *outValue = display_ptr->height();
-      break;
-    case IComposerClient::Attribute::VSYNC_PERIOD:
-      {
-        int error = 0;
-        auto display_client = display::DisplayClient::Create(&error);
-        if (!display_client) {
-          ALOGE("Could not connect to display service : %s(%d)",
-                strerror(error), error);
-          // Return a default value of 30 fps
-          *outValue = 1000 * 1000 * 1000 / 30;
-        } else {
-          auto metrics = display_client->GetDisplayMetrics();
-          *outValue = metrics.get().vsync_period_ns;
-        }
-      }
-      break;
-    case IComposerClient::Attribute::DPI_X:
-    case IComposerClient::Attribute::DPI_Y:
-      {
-        constexpr int32_t kDefaultDPI = 300;
-        int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
-        if (dpi <= 0) {
-          dpi = kDefaultDPI;
-        }
-        *outValue = 1000 * dpi;
-      }
-      break;
-    default:
-      return Error::BAD_PARAMETER;
-  }
-
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  std::vector<Config> configs(1, kDefaultConfigId);
-  *outConfigs = hidl_vec<Config>(configs);
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
-  *outName = hidl_string();
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayType(Display display,
-                            IComposerClient::DisplayType* outType) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr) {
-    *outType = IComposerClient::DisplayType::INVALID;
-    return Error::BAD_DISPLAY;
-  }
-
-  if (display == kDefaultDisplayId)
-    *outType = IComposerClient::DisplayType::PHYSICAL;
-  else
-    *outType = IComposerClient::DisplayType::VIRTUAL;
-
-  return Error::NONE;
-}
-
-Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
-  *outSupport = false;
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  return Error::NONE;
-}
-
-Error VrHwc::setActiveConfig(Display display, Config config) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-  if (config != kDefaultConfigId)
-    return Error::BAD_CONFIG;
-
-  display_ptr->set_active_config(config);
-  return Error::NONE;
-}
-
-Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (enabled != IComposerClient::Vsync::ENABLE &&
-      enabled != IComposerClient::Vsync::DISABLE) {
-    return Error::BAD_PARAMETER;
-  }
-
-  Error set_vsync_result = Error::NONE;
-  if (display == kDefaultDisplayId) {
-    sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
-        defaultServiceManager()->getService(
-            String16(IVsyncService::GetServiceName())));
-    if (vsync_service == nullptr) {
-      ALOGE("Failed to get vsync service");
-      return Error::NO_RESOURCES;
-    }
-
-    if (enabled == IComposerClient::Vsync::ENABLE) {
-      ALOGI("Enable vsync");
-      display_ptr->set_vsync_enabled(true);
-      status_t result = vsync_service->registerCallback(vsync_callback_);
-      if (result != OK) {
-        ALOGE("%s service registerCallback() failed: %s (%d)",
-            IVsyncService::GetServiceName(), strerror(-result), result);
-        set_vsync_result = Error::NO_RESOURCES;
-      }
-    } else if (enabled == IComposerClient::Vsync::DISABLE) {
-      ALOGI("Disable vsync");
-      display_ptr->set_vsync_enabled(false);
-      status_t result = vsync_service->unregisterCallback(vsync_callback_);
-      if (result != OK) {
-        ALOGE("%s service unregisterCallback() failed: %s (%d)",
-            IVsyncService::GetServiceName(), strerror(-result), result);
-        set_vsync_result = Error::NO_RESOURCES;
-      }
-    }
-
-    UpdateVsyncCallbackEnabledLocked();
-  }
-
-  return set_vsync_result;
-}
-
-Error VrHwc::setColorTransform(Display display, const float* matrix,
-                               int32_t hint) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->SetColorTransform(matrix, hint);
-  return Error::NONE;
-}
-
-Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
-                             int32_t acquireFence, int32_t /* dataspace */,
-                             const std::vector<hwc_rect_t>& /* damage */) {
-  base::unique_fd fence(acquireFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (target == nullptr)
-    return Error::NONE;
-
-  if (!display_ptr->SetClientTarget(target, std::move(fence)))
-    return Error::BAD_PARAMETER;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
-                             int32_t releaseFence) {
-  base::unique_fd fence(releaseFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  // TODO(dnicoara): Is it necessary to do anything here?
-  return Error::NONE;
-}
-
-Error VrHwc::validateDisplay(
-    Display display, std::vector<Layer>* outChangedLayers,
-    std::vector<IComposerClient::Composition>* outCompositionTypes,
-    uint32_t* /* outDisplayRequestMask */,
-    std::vector<Layer>* /* outRequestedLayers */,
-    std::vector<uint32_t>* /* outRequestMasks */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->GetChangedCompositionTypes(outChangedLayers,
-                                          outCompositionTypes);
-  return Error::NONE;
-}
-
-Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
-
-Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
-                            std::vector<Layer>* outLayers,
-                            std::vector<int32_t>* outReleaseFences) {
-  *outPresentFence = -1;
-  outLayers->clear();
-  outReleaseFences->clear();
-
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  ComposerView::Frame frame;
-  std::vector<Layer> last_frame_layers;
-  Error status = display_ptr->GetFrame(&frame.layers);
-  frame.display_id = display;
-  frame.display_width = display_ptr->width();
-  frame.display_height = display_ptr->height();
-  frame.active_config = display_ptr->active_config();
-  frame.power_mode = display_ptr->power_mode();
-  frame.vsync_enabled = display_ptr->vsync_enabled() ?
-      IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
-  frame.color_transform_hint = display_ptr->color_transform_hint();
-  frame.color_mode = display_ptr->color_mode();
-  memcpy(frame.color_transform, display_ptr->color_transform(),
-         sizeof(frame.color_transform));
-  if (status != Error::NONE)
-    return status;
-
-  last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
-
-  base::unique_fd fence;
-  if (observer_)
-    fence = observer_->OnNewFrame(frame);
-
-  if (fence.get() < 0)
-    return Error::NONE;
-
-  *outPresentFence = dup(fence.get());
-  outLayers->swap(last_frame_layers);
-  for (size_t i = 0; i < outLayers->size(); ++i)
-    outReleaseFences->push_back(dup(fence.get()));
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
-                                    int32_t y) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.cursor_x = x;
-  hwc_layer->info.cursor_y = y;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBuffer(Display display, Layer layer,
-                            buffer_handle_t buffer, int32_t acquireFence) {
-  base::unique_fd fence(acquireFence);
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.buffer = CreateGraphicBuffer(
-      buffer, hwc_layer->buffer_metadata);
-  hwc_layer->info.fence = new Fence(fence.release());
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
-                                   const std::vector<hwc_rect_t>& damage) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.damaged_regions = damage;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.blend_mode =
-      static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerColor(Display display, Layer layer,
-                           IComposerClient::Color color) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.color = color;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerCompositionType(Display display, Layer layer,
-                                     int32_t type) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerDataspace(Display display, Layer layer,
-                               int32_t dataspace) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.dataspace = dataspace;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
-                                  const hwc_rect_t& frame) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.display_frame =
-      {frame.left, frame.top, frame.right, frame.bottom};
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.alpha = alpha;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
-                                    buffer_handle_t /* stream */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
-                                const hwc_frect_t& crop) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerTransform(Display display, Layer layer,
-                               int32_t transform) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.transform = transform;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
-                                   const std::vector<hwc_rect_t>& visible) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.visible_regions = visible;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.z_order = z;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
-                          uint32_t appId) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->info.type = type;
-  hwc_layer->info.app_id = appId;
-
-  return Error::NONE;
-}
-
-Error VrHwc::setClientTargetMetadata(
-    Display display, const IVrComposerClient::BufferMetadata& metadata) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  display_ptr->SetClientTargetMetadata(metadata);
-
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerBufferMetadata(
-    Display display, Layer layer,
-    const IVrComposerClient::BufferMetadata& metadata) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
-  if (!hwc_layer)
-    return Error::BAD_LAYER;
-
-  hwc_layer->buffer_metadata = metadata;
-
-  return Error::NONE;
-}
-
-Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) {
-  hidl_cb(hidl_vec<Capability>());
-  return Void();
-}
-
-Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-  std::string result;
-
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    result = "\nVrHwc states:\n";
-    for (const auto& pair : displays_) {
-      result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first);
-      pair.second->dumpDebugInfo(&result);
-    }
-    result += "\n";
-  }
-
-  hidl_cb(hidl_string(result));
-  return Void();
-}
-
-Return<void> VrHwc::createClient(createClient_cb hidl_cb) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  Error status = Error::NONE;
-  sp<VrComposerClient> client;
-  if (!client_.promote().get()) {
-    client = new VrComposerClient(*this);
-  } else {
-    ALOGE("Already have a client");
-    status = Error::NO_RESOURCES;
-  }
-
-  client_ = client;
-  hidl_cb(status, client);
-  return Void();
-}
-
-Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  Error status = Error::NONE;
-  sp<VrComposerClient> client;
-  if (!client_.promote().get()) {
-    client = new VrComposerClient(*this);
-  } else {
-    ALOGE("Already have a client");
-    status = Error::NO_RESOURCES;
-  }
-
-  client_ = client;
-  hidl_cb(status, client);
-  return Void();
-}
-
-void VrHwc::ForceDisplaysRefresh() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (event_callback_ != nullptr) {
-    for (const auto& pair : displays_)
-      event_callback_->onRefresh(pair.first);
-  }
-}
-
-void VrHwc::RegisterObserver(Observer* observer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (observer_)
-    ALOGE("Overwriting observer");
-  else
-    observer_ = observer;
-}
-
-void VrHwc::UnregisterObserver(Observer* observer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (observer != observer_)
-    ALOGE("Trying to unregister unknown observer");
-  else
-    observer_ = nullptr;
-}
-
-HwcDisplay* VrHwc::FindDisplay(Display display) {
-  auto iter = displays_.find(display);
-  return iter == displays_.end() ? nullptr : iter->second.get();
-}
-
-void VrHwc::UpdateVsyncCallbackEnabledLocked() {
-  auto primary_display = FindDisplay(kDefaultDisplayId);
-  LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
-      "Should have created the primary display by now");
-  bool send_vsync =
-      event_callback_ != nullptr && primary_display->vsync_enabled();
-  vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
-}
-
-Return<void> VrHwc::debug(const hidl_handle& fd,
-                          const hidl_vec<hidl_string>& args) {
-  std::string result;
-
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    for (const auto& pair : displays_) {
-      result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
-      pair.second->dumpDebugInfo(&result);
-    }
-    result += "\n";
-  }
-
-  FILE* out = fdopen(dup(fd->data[0]), "w");
-  fprintf(out, "%s", result.c_str());
-  fclose(out);
-
-  return Void();
-}
-
-void HwcLayer::dumpDebugInfo(std::string* result) const {
-  if (!result) {
-    return;
-  }
-  *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\
-      cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\
-      display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n",
-      composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y,
-      info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform,
-      info.display_frame.left, info.display_frame.top, info.display_frame.right,
-      info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right,
-      info.crop.bottom, info.blend_mode);
-  *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\
-      , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride,
-      buffer_metadata.layerCount, buffer_metadata.format);
-}
-
-status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
-  ATRACE_NAME("vr_hwc onVsync");
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (callback_ != nullptr)
-    callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
-  return OK;
-}
-
-void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  callback_ = callback;
-}
-
-// composer::V2_2::ComposerHal
-Error VrHwc::setReadbackBuffer(Display display,
-                               const native_handle_t* bufferHandle,
-                               android::base::unique_fd fenceFd) {
-  return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferFence(Display display,
-                                    android::base::unique_fd* outFenceFd) {
-  return Error::NONE;
-}
-
-Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                      types::V1_1::PixelFormat* format,
-                                      Display* outDisplay) {
-  *format = types::V1_1::PixelFormat::RGBA_8888;
-  *outDisplay = display_count_;
-  displays_[display_count_].reset(new HwcDisplay(width, height));
-  display_count_++;
-  return Error::NONE;
-}
-
-Error VrHwc::setPowerMode_2_2(Display display,
-                              IComposerClient::PowerMode mode) {
-  bool dozeSupported = false;
-
-  Error dozeSupportError = getDozeSupport(display, &dozeSupported);
-
-  if (dozeSupportError != Error::NONE)
-    return dozeSupportError;
-
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < IComposerClient::PowerMode::OFF ||
-      mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
-    return Error::BAD_PARAMETER;
-  }
-
-  if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE ||
-                         mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
-    return Error::UNSUPPORTED;
-  }
-
-  display_ptr->set_power_mode(mode);
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerFloatColor(Display display, Layer layer,
-                                IComposerClient::FloatColor color) {
-  return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
-                              std::vector<RenderIntent>* outIntents) {
-  return Error::NONE;
-}
-
-std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
-    types::V1_1::Dataspace dataspace) {
-  return {};
-}
-
-// composer::V2_3::ComposerHal
-Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
-                                    hidl_vec<Hdr>* /*outTypes*/,
-                                    float* outMaxLuminance,
-                                    float* outMaxAverageLuminance,
-                                    float* outMinLuminance) {
-  *outMaxLuminance = 0;
-  *outMaxAverageLuminance = 0;
-  *outMinLuminance = 0;
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadata_2_3(
-    Display display, Layer layer,
-    const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
-  return Error::NONE;
-}
-
-Error VrHwc::getPerFrameMetadataKeys_2_3(
-    Display display,
-    std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
-  return Error::NONE;
-}
-
-Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
-                              RenderIntent intent) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
-    return Error::BAD_PARAMETER;
-
-  display_ptr->set_color_mode(mode);
-  return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode,
-                                  std::vector<RenderIntent>* outIntents) {
-  return Error::NONE;
-}
-
-Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
-  return Error::NONE;
-}
-
-Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
-                                        uint32_t height, PixelFormat format,
-                                        Dataspace dataspace) {
-  return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
-                                             PixelFormat* outFormat,
-                                             Dataspace* outDataspace) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                          std::vector<uint8_t>* outData) {
-  int error = 0;
-  auto display_client = display::DisplayClient::Create(&error);
-  if (!display_client) {
-    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
-          error);
-    return Error::BAD_CONFIG;
-  }
-  auto edid_data = display_client->GetConfigurationData(
-      display::ConfigFileType::kDeviceEdid);
-  auto display_identification_port =
-      display_client->GetDisplayIdentificationPort();
-  *outPort = display_identification_port.get();
-
-  std::copy(edid_data.get().begin(), edid_data.get().end(),
-            std::back_inserter(*outData));
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerColorTransform(Display display, Layer layer,
-                                    const float* matrix) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSamplingAttributes(
-    Display display, PixelFormat& format, Dataspace& dataspace,
-    hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
-  return Error::NONE;
-}
-
-Error VrHwc::setDisplayedContentSamplingEnabled(
-    Display display, IComposerClient::DisplayedContentSampling enable,
-    hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
-    uint64_t maxFrames) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
-                                       uint64_t timestamp, uint64_t& frameCount,
-                                       hidl_vec<uint64_t>& sampleComponent0,
-                                       hidl_vec<uint64_t>& sampleComponent1,
-                                       hidl_vec<uint64_t>& sampleComponent2,
-                                       hidl_vec<uint64_t>& sampleComponent3) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayCapabilities(
-    Display display,
-    std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
-  return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadataBlobs(
-    Display display, Layer layer,
-    std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
-  return Error::NONE;
-}
-
-Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
-  return Error::NONE;
-}
-
-Error VrHwc::setDisplayBrightness(Display display, float brightness) {
-  return Error::NONE;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
deleted file mode 100644
index 3e3a630..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-
-#include <android-base/unique_fd.h>
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <composer-hal/2.3/ComposerHal.h>
-#include <private/dvr/vsync_service.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/StrongPointer.h>
-
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::frameworks::vr::composer::V2_0;
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::hardware::hidl_bitfield;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::graphics::composer::V2_1::Config;
-using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
-using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
-
-namespace android {
-
-class Fence;
-
-namespace dvr {
-
-class VrComposerClient;
-
-using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
-
-namespace types = android::hardware::graphics::common;
-
-using types::V1_1::RenderIntent;
-using types::V1_2::ColorMode;
-using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
-using types::V1_2::PixelFormat;
-
-class ComposerView {
- public:
-  struct ComposerLayer {
-    using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
-    using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
-    using BlendMode =
-        hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
-
-    Layer id;
-    sp<GraphicBuffer> buffer;
-    sp<Fence> fence;
-    Recti display_frame;
-    Rectf crop;
-    BlendMode blend_mode;
-    float alpha;
-    uint32_t type;
-    uint32_t app_id;
-    uint32_t z_order;
-    int32_t cursor_x;
-    int32_t cursor_y;
-    IComposerClient::Color color;
-    int32_t dataspace;
-    int32_t transform;
-    std::vector<hwc_rect_t> visible_regions;
-    std::vector<hwc_rect_t> damaged_regions;
-  };
-
-  struct Frame {
-    Display display_id;
-    // This is set to true to notify the upper layer that the display is
-    // being removed, or left false in the case of a normal frame. The upper
-    // layer tracks display IDs and will handle new ones showing up.
-    bool removed = false;
-    int32_t display_width;
-    int32_t display_height;
-    Config active_config;
-    ColorMode color_mode;
-    IComposerClient::PowerMode power_mode;
-    IComposerClient::Vsync vsync_enabled;
-    float color_transform[16];
-    int32_t color_transform_hint;
-    std::vector<ComposerLayer> layers;
-  };
-
-  class Observer {
-   public:
-    virtual ~Observer() {}
-
-    // Returns a list of layers that need to be shown together. Layers are
-    // returned in z-order, with the lowest layer first.
-    virtual base::unique_fd OnNewFrame(const Frame& frame) = 0;
-  };
-
-  virtual ~ComposerView() {}
-
-  virtual void ForceDisplaysRefresh() = 0;
-  virtual void RegisterObserver(Observer* observer) = 0;
-  virtual void UnregisterObserver(Observer* observer) = 0;
-};
-
-struct HwcLayer {
-  using Composition =
-      hardware::graphics::composer::V2_3::IComposerClient::Composition;
-
-  explicit HwcLayer(Layer new_id) { info.id = new_id; }
-
-  void dumpDebugInfo(std::string* result) const;
-
-  Composition composition_type;
-  ComposerView::ComposerLayer info;
-  IVrComposerClient::BufferMetadata buffer_metadata;
-};
-
-class HwcDisplay {
- public:
-  HwcDisplay(int32_t width, int32_t height);
-  ~HwcDisplay();
-
-  int32_t width() const { return width_; }
-  int32_t height() const { return height_; }
-
-  HwcLayer* CreateLayer();
-  bool DestroyLayer(Layer id);
-  HwcLayer* GetLayer(Layer id);
-
-  bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
-  void SetClientTargetMetadata(
-      const IVrComposerClient::BufferMetadata& metadata);
-
-  void GetChangedCompositionTypes(
-      std::vector<Layer>* layer_ids,
-      std::vector<IComposerClient::Composition>* composition);
-
-  Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
-
-  std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
-
-  Config active_config() const { return active_config_; }
-  void set_active_config(Config config) { active_config_ = config; }
-
-  ColorMode color_mode() const { return color_mode_; }
-  void set_color_mode(ColorMode mode) { color_mode_ = mode; }
-
-  IComposerClient::PowerMode power_mode() const { return power_mode_; }
-  void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
-
-  bool vsync_enabled() const { return vsync_enabled_; }
-  void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
-
-  const float* color_transform() const { return color_transform_; }
-  int32_t color_transform_hint() const { return color_transform_hint_; }
-  void SetColorTransform(const float* matrix, int32_t hint);
-
-  void dumpDebugInfo(std::string* result) const;
-
- private:
-  // The client target buffer and the associated fence.
-  sp<GraphicBuffer> buffer_;
-  IVrComposerClient::BufferMetadata buffer_metadata_;
-  sp<Fence> fence_;
-
-  // List of currently active layers.
-  std::vector<HwcLayer> layers_;
-
-  std::vector<Layer> last_frame_layers_ids_;
-
-  // Layer ID generator.
-  uint64_t layer_ids_ = 1;
-
-  int32_t width_;
-  int32_t height_;
-
-  Config active_config_;
-  ColorMode color_mode_;
-  IComposerClient::PowerMode power_mode_;
-  bool vsync_enabled_ = false;
-  float color_transform_[16];
-  int32_t color_transform_hint_;
-
-  HwcDisplay(const HwcDisplay&) = delete;
-  void operator=(const HwcDisplay&) = delete;
-};
-
-class VrHwc : public IComposer, public ComposerHal, public ComposerView {
- public:
-  VrHwc();
-  ~VrHwc() override;
-
-  Error setLayerInfo(Display display, Layer layer, uint32_t type,
-                     uint32_t appId);
-  Error setClientTargetMetadata(
-      Display display, const IVrComposerClient::BufferMetadata& metadata);
-  Error setLayerBufferMetadata(
-      Display display, Layer layer,
-      const IVrComposerClient::BufferMetadata& metadata);
-
-  // composer::V2_1::ComposerHal
-  bool hasCapability(hwc2_capability_t capability) override;
-
-  std::string dumpDebugInfo() override { return {}; }
-
-  void registerEventCallback(ComposerHal::EventCallback* callback) override;
-  void unregisterEventCallback() override;
-
-  uint32_t getMaxVirtualDisplayCount() override;
-  Error destroyVirtualDisplay(Display display) override;
-
-  Error createLayer(Display display, Layer* outLayer) override;
-  Error destroyLayer(Display display, Layer layer) override;
-
-  Error getActiveConfig(Display display, Config* outConfig) override;
-  Error getDisplayAttribute(Display display, Config config,
-                            IComposerClient::Attribute attribute,
-                            int32_t* outValue) override;
-  Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-  Error getDisplayName(Display display, hidl_string* outName) override;
-  Error getDisplayType(Display display,
-                       IComposerClient::DisplayType* outType) override;
-  Error getDozeSupport(Display display, bool* outSupport) override;
-
-  Error setActiveConfig(Display display, Config config) override;
-  Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
-  Error setColorTransform(Display display, const float* matrix,
-                          int32_t hint) override;
-  Error setClientTarget(Display display, buffer_handle_t target,
-                        int32_t acquireFence, int32_t dataspace,
-                        const std::vector<hwc_rect_t>& damage) override;
-  Error setOutputBuffer(Display display, buffer_handle_t buffer,
-                        int32_t releaseFence) override;
-  Error validateDisplay(
-      Display display, std::vector<Layer>* outChangedLayers,
-      std::vector<IComposerClient::Composition>* outCompositionTypes,
-      uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-      std::vector<uint32_t>* outRequestMasks) override;
-  Error acceptDisplayChanges(Display display) override;
-  Error presentDisplay(Display display, int32_t* outPresentFence,
-                       std::vector<Layer>* outLayers,
-                       std::vector<int32_t>* outReleaseFences) override;
-
-  Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
-                               int32_t y) override;
-  Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                       int32_t acquireFence) override;
-  Error setLayerSurfaceDamage(Display display, Layer layer,
-                              const std::vector<hwc_rect_t>& damage) override;
-  Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-  Error setLayerColor(Display display, Layer layer,
-                      IComposerClient::Color color) override;
-  Error setLayerCompositionType(Display display, Layer layer,
-                                int32_t type) override;
-  Error setLayerDataspace(Display display, Layer layer,
-                          int32_t dataspace) override;
-  Error setLayerDisplayFrame(Display display, Layer layer,
-                             const hwc_rect_t& frame) override;
-  Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-  Error setLayerSidebandStream(Display display, Layer layer,
-                               buffer_handle_t stream) override;
-  Error setLayerSourceCrop(Display display, Layer layer,
-                           const hwc_frect_t& crop) override;
-  Error setLayerTransform(Display display, Layer layer,
-                          int32_t transform) override;
-  Error setLayerVisibleRegion(Display display, Layer layer,
-                              const std::vector<hwc_rect_t>& visible) override;
-  Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
-  // composer::V2_2::ComposerHal
-  Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
-                          android::base::unique_fd fenceFd) override;
-  Error getReadbackBufferFence(Display display,
-                               android::base::unique_fd* outFenceFd) override;
-  Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                 types::V1_1::PixelFormat* format,
-                                 Display* outDisplay) override;
-  Error setPowerMode_2_2(Display display,
-                         IComposerClient::PowerMode mode) override;
-  Error setLayerFloatColor(Display display, Layer layer,
-                           IComposerClient::FloatColor color) override;
-  Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
-                         std::vector<RenderIntent>* outIntents) override;
-  std::array<float, 16> getDataspaceSaturationMatrix(
-      types::V1_1::Dataspace dataspace) override;
-
-  // composer::V2_3::ComposerHal
-  Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
-                               float* outMaxLuminance,
-                               float* outMaxAverageLuminance,
-                               float* outMinLuminance) override;
-  Error setLayerPerFrameMetadata_2_3(
-      Display display, Layer layer,
-      const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
-  Error getPerFrameMetadataKeys_2_3(
-      Display display,
-      std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
-  Error setColorMode_2_3(Display display, ColorMode mode,
-                         RenderIntent intent) override;
-  Error getRenderIntents_2_3(Display display, ColorMode mode,
-                             std::vector<RenderIntent>* outIntents) override;
-  Error getColorModes_2_3(Display display,
-                          hidl_vec<ColorMode>* outModes) override;
-  Error getClientTargetSupport_2_3(Display display, uint32_t width,
-                                   uint32_t height, PixelFormat format,
-                                   Dataspace dataspace) override;
-  Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
-                                        Dataspace* outDataspace) override;
-  Error getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                     std::vector<uint8_t>* outData) override;
-  Error setLayerColorTransform(Display display, Layer layer,
-                               const float* matrix) override;
-  Error getDisplayedContentSamplingAttributes(
-      Display display, PixelFormat& format, Dataspace& dataspace,
-      hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
-      override;
-  Error setDisplayedContentSamplingEnabled(
-      Display display, IComposerClient::DisplayedContentSampling enable,
-      hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
-      uint64_t maxFrames) override;
-  Error getDisplayedContentSample(
-      Display display, uint64_t maxFrames, uint64_t timestamp,
-      uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
-      hidl_vec<uint64_t>& sampleComponent1,
-      hidl_vec<uint64_t>& sampleComponent2,
-      hidl_vec<uint64_t>& sampleComponent3) override;
-  Error getDisplayCapabilities(Display display,
-                               std::vector<IComposerClient::DisplayCapability>*
-                                   outCapabilities) override;
-  Error setLayerPerFrameMetadataBlobs(
-      Display display, Layer layer,
-      std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
-  Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
-  Error setDisplayBrightness(Display display, float brightness) override;
-
-  // IComposer:
-  Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-  Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-  Return<void> createClient(createClient_cb hidl_cb) override;
-  Return<void> createClient_2_3(
-      IComposer::createClient_2_3_cb hidl_cb) override;
-
-  // ComposerView:
-  void ForceDisplaysRefresh() override;
-  void RegisterObserver(Observer* observer) override;
-  void UnregisterObserver(Observer* observer) override;
-
-  Return<void> debug(const hidl_handle& fd,
-                     const hidl_vec<hidl_string>& args) override;
-
- private:
-  class VsyncCallback : public BnVsyncCallback {
-   public:
-    status_t onVsync(int64_t vsync_timestamp) override;
-    void SetEventCallback(EventCallback* callback);
-   private:
-    std::mutex mutex_;
-    EventCallback* callback_;
-  };
-
-  HwcDisplay* FindDisplay(Display display);
-
-  // Re-evaluate whether or not we should start making onVsync() callbacks to
-  // the client. We need enableCallback(true) to have been called, and
-  // setVsyncEnabled() to have been called for the primary display. The caller
-  // must have mutex_ locked already.
-  void UpdateVsyncCallbackEnabledLocked();
-
-  wp<VrComposerClient> client_;
-
-  // Guard access to internal state from binder threads.
-  std::mutex mutex_;
-
-  std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
-  Display display_count_ = 2;
-
-  EventCallback* event_callback_ = nullptr;
-  Observer* observer_ = nullptr;
-
-  sp<VsyncCallback> vsync_callback_;
-
-  VrHwc(const VrHwc&) = delete;
-  void operator=(const VrHwc&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp
deleted file mode 100644
index 2e70928..0000000
--- a/services/vr/hardware_composer/tests/vr_composer_test.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <android/dvr/BnVrComposerCallback.h>
-#include <binder/IServiceManager.h>
-#include <gtest/gtest.h>
-#include <sys/eventfd.h>
-#include <vr_composer.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-const char kVrDisplayName[] = "VrDisplay_Test";
-
-class TestComposerView : public ComposerView {
- public:
-  TestComposerView() {}
-  ~TestComposerView() override = default;
-
-  size_t display_refresh_count() const { return display_refresh_count_; }
-
-  void ForceDisplaysRefresh() override { display_refresh_count_++; }
-  void RegisterObserver(Observer* observer) override {}
-  void UnregisterObserver(Observer* observer) override {}
-
-  TestComposerView(const TestComposerView&) = delete;
-  void operator=(const TestComposerView&) = delete;
-
- private:
-  size_t display_refresh_count_ = 0;
-};
-
-class TestComposerCallback : public BnVrComposerCallback {
- public:
-  TestComposerCallback() {}
-  ~TestComposerCallback() override = default;
-
-  ComposerView::Frame last_frame() const { return last_frame_; }
-
-  binder::Status onNewFrame(
-      const ParcelableComposerFrame& frame,
-      ParcelableUniqueFd* /* fence */) override {
-    last_frame_ = frame.frame();
-    return binder::Status::ok();
-  }
-
- private:
-  ComposerView::Frame last_frame_;
-
-  TestComposerCallback(const TestComposerCallback&) = delete;
-  void operator=(const TestComposerCallback&) = delete;
-};
-
-class TestComposerCallbackWithFence : public TestComposerCallback {
- public:
-  ~TestComposerCallbackWithFence() override = default;
-
-  binder::Status onNewFrame(
-      const ParcelableComposerFrame& frame,
-      ParcelableUniqueFd* fence) override {
-    binder::Status status = TestComposerCallback::onNewFrame(frame, fence);
-
-    base::unique_fd fd(eventfd(0, 0));
-    EXPECT_LE(0, fd.get());
-    fence->set_fence(fd);
-
-    return status;
-  }
-};
-
-sp<GraphicBuffer> CreateBuffer() {
-  return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888,
-                           GraphicBuffer::USAGE_HW_TEXTURE);
-}
-
-}  // namespace
-
-class VrComposerTest : public testing::Test {
- public:
-  VrComposerTest() : composer_(new VrComposer(&composer_view_)) {}
-  ~VrComposerTest() override = default;
-
-  sp<IVrComposer> GetComposerProxy() const {
-    sp<IServiceManager> sm(defaultServiceManager());
-    return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName)));
-  }
-
-  void SetUp() override {
-    sp<IServiceManager> sm(defaultServiceManager());
-    EXPECT_EQ(OK,
-              sm->addService(String16(kVrDisplayName), composer_, false));
-  }
-
- protected:
-  TestComposerView composer_view_;
-  sp<VrComposer> composer_;
-
-  VrComposerTest(const VrComposerTest&) = delete;
-  void operator=(const VrComposerTest&) = delete;
-};
-
-TEST_F(VrComposerTest, TestWithoutObserver) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  ComposerView::Frame frame;
-
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithObserver) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  sp<TestComposerCallback> callback = new TestComposerCallback();
-  ASSERT_EQ(0, composer_view_.display_refresh_count());
-  ASSERT_TRUE(composer->registerObserver(callback).isOk());
-  ASSERT_EQ(1, composer_view_.display_refresh_count());
-
-  ComposerView::Frame frame;
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithOneLayer) {
-  sp<IVrComposer> composer = GetComposerProxy();
-  sp<TestComposerCallback> callback = new TestComposerCallbackWithFence();
-  ASSERT_TRUE(composer->registerObserver(callback).isOk());
-
-  ComposerView::Frame frame;
-  frame.display_id = 1;
-  frame.removed = false;
-  frame.display_width = 600;
-  frame.display_height = 400;
-  frame.layers.push_back(ComposerView::ComposerLayer{
-    .id = 1,
-    .buffer = CreateBuffer(),
-    .fence = new Fence(eventfd(0, 0)),
-    .display_frame = {0, 0, 600, 400},
-    .crop = {0.0f, 0.0f, 600.0f, 400.0f},
-    .blend_mode = IComposerClient::BlendMode::NONE,
-    .alpha = 1.0f,
-    .type = 1,
-    .app_id = 1,
-  });
-  base::unique_fd fence = composer_->OnNewFrame(frame);
-  ASSERT_LE(0, fence.get());
-
-  ComposerView::Frame received_frame = callback->last_frame();
-  ASSERT_EQ(frame.display_id, received_frame.display_id);
-  ASSERT_EQ(frame.display_width, received_frame.display_width);
-  ASSERT_EQ(frame.display_height, received_frame.display_height);
-  ASSERT_EQ(frame.removed, received_frame.removed);
-  ASSERT_EQ(1u, received_frame.layers.size());
-  ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id);
-  ASSERT_NE(nullptr, received_frame.layers[0].buffer.get());
-  ASSERT_TRUE(received_frame.layers[0].fence->isValid());
-  ASSERT_EQ(frame.layers[0].display_frame.left,
-            received_frame.layers[0].display_frame.left);
-  ASSERT_EQ(frame.layers[0].display_frame.top,
-            received_frame.layers[0].display_frame.top);
-  ASSERT_EQ(frame.layers[0].display_frame.right,
-            received_frame.layers[0].display_frame.right);
-  ASSERT_EQ(frame.layers[0].display_frame.bottom,
-            received_frame.layers[0].display_frame.bottom);
-  ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left);
-  ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top);
-  ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right);
-  ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom);
-  ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode);
-  ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha);
-  ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type);
-  ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp
deleted file mode 100644
index d93f370..0000000
--- a/services/vr/hardware_composer/vr_composer.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "vr_composer.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionCache.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-bool CheckPermission() {
-  const android::IPCThreadState* ipc = android::IPCThreadState::self();
-  const pid_t pid = ipc->getCallingPid();
-  const uid_t uid = ipc->getCallingUid();
-  const bool permission = PermissionCache::checkPermission(
-      String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid);
-  if (!permission)
-    ALOGE("permission denied to pid=%d uid=%u", pid, uid);
-
-  return permission;
-}
-
-}  // namespace
-
-VrComposer::VrComposer(ComposerView* composer_view)
-  : composer_view_(composer_view) {
-  composer_view_->RegisterObserver(this);
-}
-
-VrComposer::~VrComposer() {
-  composer_view_->UnregisterObserver(this);
-}
-
-binder::Status VrComposer::registerObserver(
-    const sp<IVrComposerCallback>& callback) {
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    if (!CheckPermission())
-      return binder::Status::fromStatusT(PERMISSION_DENIED);
-
-    if (callback_.get()) {
-      ALOGE("Failed to register callback, already registered");
-      return binder::Status::fromStatusT(ALREADY_EXISTS);
-    }
-
-    callback_ = callback;
-    IInterface::asBinder(callback_)->linkToDeath(this);
-  }
-
-  // Don't take the lock to force display refresh otherwise it could end in a
-  // deadlock since HWC calls this with new frames and it has a lock of its own
-  // to serialize access to the display information.
-  composer_view_->ForceDisplaysRefresh();
-  return binder::Status::ok();
-}
-
-binder::Status VrComposer::clearObserver() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  callback_ = nullptr;
-  return binder::Status::ok();
-}
-
-base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  if (!callback_.get())
-    return base::unique_fd();
-
-  ParcelableComposerFrame parcelable_frame(frame);
-  ParcelableUniqueFd fence;
-  binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence);
-  if (!ret.isOk())
-    ALOGE("Failed to send new frame: %s", ret.toString8().string());
-
-  return fence.fence();
-}
-
-void VrComposer::binderDied(const wp<IBinder>& /* who */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  callback_ = nullptr;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h
deleted file mode 100644
index 1273352..0000000
--- a/services/vr/hardware_composer/vr_composer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-
-#include <android/dvr/BnVrComposer.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class VrComposerCallback;
-
-// Implementation of the IVrComposer service used to notify VR Window Manager
-// when SurfaceFlinger presents 2D UI changes.
-//
-// VR HWC updates the presented frame via the ComposerView::Observer interface.
-// On notification |callback_| is called to update VR Window Manager.
-// NOTE: If VR Window Manager isn't connected, the notification is a no-op.
-class VrComposer
-    : public BnVrComposer,
-      public ComposerView::Observer,
-      public IBinder::DeathRecipient {
- public:
-  explicit VrComposer(ComposerView* composer_view);
-  ~VrComposer() override;
-
-  // BnVrComposer:
-  binder::Status registerObserver(
-      const sp<IVrComposerCallback>& callback) override;
-
-  binder::Status clearObserver() override;
-
-  // ComposerView::Observer:
-  base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
-
- private:
-  // IBinder::DeathRecipient:
-  void binderDied(const wp<IBinder>& who) override;
-
-  std::mutex mutex_;
-
-  sp<IVrComposerCallback> callback_;
-
-  ComposerView* composer_view_;  // Not owned.
-
-  VrComposer(const VrComposer&) = delete;
-  void operator=(const VrComposer&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  //  ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
         "vkGetPhysicalDeviceMemoryProperties2",
         "vkGetPhysicalDeviceMemoryProperties2KHR",
         "vkGetPhysicalDeviceMultisamplePropertiesEXT",
+        "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
         "vkGetPhysicalDevicePresentRectanglesKHR",
         "vkGetPhysicalDeviceProperties",
         "vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4927150..a99355f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,8 +1027,11 @@
     }
 }
 
-bool GetAndroidNativeBufferSpecVersion9Support(
-    VkPhysicalDevice physicalDevice) {
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+    VkPhysicalDevice physicalDevice,
+    bool& support) {
+    support = false;
+
     const InstanceData& data = GetData(physicalDevice);
 
     // Call to get propertyCount
@@ -1038,6 +1041,10 @@
         physicalDevice, nullptr, &propertyCount, nullptr);
     ATRACE_END();
 
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
+
     // Call to enumerate properties
     std::vector<VkExtensionProperties> properties(propertyCount);
     ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
@@ -1045,6 +1052,10 @@
         physicalDevice, nullptr, &propertyCount, properties.data());
     ATRACE_END();
 
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
+
     for (uint32_t i = 0; i < propertyCount; i++) {
         auto& prop = properties[i];
 
@@ -1053,11 +1064,12 @@
             continue;
 
         if (prop.specVersion >= 9) {
-            return true;
+            support = true;
+            return result;
         }
     }
 
-    return false;
+    return result;
 }
 
 VkResult EnumerateDeviceExtensionProperties(
@@ -1101,18 +1113,30 @@
     swapchainCompFeats.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
     swapchainCompFeats.pNext = nullptr;
+    swapchainCompFeats.imageCompressionControlSwapchain = false;
     VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
     imageCompFeats.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
     imageCompFeats.pNext = &swapchainCompFeats;
+    imageCompFeats.imageCompressionControl = false;
 
     VkPhysicalDeviceFeatures2 feats2 = {};
     feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
     feats2.pNext = &imageCompFeats;
 
-    GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+    const auto& driver = GetData(physicalDevice).driver;
+    if (driver.GetPhysicalDeviceFeatures2 ||
+        driver.GetPhysicalDeviceFeatures2KHR) {
+        GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+    }
 
-    bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+    bool anb9 = false;
+    VkResult result =
+        GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
 
     if (anb9 && imageCompFeats.imageCompressionControl) {
         loader_extensions.push_back(
@@ -1142,7 +1166,7 @@
     }
 
     ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
-    VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+    result = data.driver.EnumerateDeviceExtensionProperties(
         physicalDevice, pLayerName, pPropertyCount, pProperties);
     ATRACE_END();
 
@@ -1532,6 +1556,11 @@
             } break;
 
             case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+                VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+                    compressionFeat = reinterpret_cast<
+                        VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+                        pFeats);
+                compressionFeat->imageCompressionControlSwapchain = false;
                 imageCompressionControlSwapchainInChain = true;
             } break;
 
@@ -1551,6 +1580,7 @@
         imageCompFeats.sType =
             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
         imageCompFeats.pNext = nullptr;
+        imageCompFeats.imageCompressionControl = false;
 
         VkPhysicalDeviceFeatures2 feats2 = {};
         feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 475bc40..c7284ce 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -243,6 +243,11 @@
 // syncronous requests to Surface Flinger):
 enum { MIN_NUM_FRAMES_AGO = 5 };
 
+bool IsSharedPresentMode(VkPresentModeKHR mode) {
+    return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+        mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
+}
+
 struct Swapchain {
     Swapchain(Surface& surface_,
               uint32_t num_images_,
@@ -254,9 +259,7 @@
           pre_transform(pre_transform_),
           frame_timestamps_enabled(false),
           acquire_next_image_timeout(-1),
-          shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
-                 present_mode ==
-                     VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+          shared(IsSharedPresentMode(present_mode)) {
         ANativeWindow* window = surface.window.get();
         native_window_get_refresh_cycle_duration(
             window,
@@ -930,27 +933,36 @@
         return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
                                                   pSurfaceInfo->surface,
                                                   pSurfaceFormatCount, nullptr);
-    } else {
-        // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
-        // after the call.
-        std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
-        VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
-            physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
-            surface_formats.data());
+    }
 
-        if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
-            const auto& driver = GetData(physicalDevice).driver;
+    // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+    // after the call.
+    std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+    VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+        physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+        surface_formats.data());
 
-            // marshal results individually due to stride difference.
-            uint32_t formats_to_marshal = *pSurfaceFormatCount;
-            for (uint32_t i = 0u; i < formats_to_marshal; i++) {
-                pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+        return result;
+    }
 
-                // Query the compression properties for the surface format
-                if (pSurfaceFormats[i].pNext) {
+    const auto& driver = GetData(physicalDevice).driver;
+
+    // marshal results individually due to stride difference.
+    uint32_t formats_to_marshal = *pSurfaceFormatCount;
+    for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+        pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+        // Query the compression properties for the surface format
+        VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+        while (pSurfaceFormat->pNext) {
+            pSurfaceFormat =
+                reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+            switch (pSurfaceFormat->sType) {
+                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
                     VkImageCompressionPropertiesEXT* surfaceCompressionProps =
                         reinterpret_cast<VkImageCompressionPropertiesEXT*>(
-                            pSurfaceFormats[i].pNext);
+                            pSurfaceFormat);
 
                     if (surfaceCompressionProps &&
                         driver.GetPhysicalDeviceImageFormatProperties2KHR) {
@@ -992,12 +1004,16 @@
                             return compressionRes;
                         }
                     }
-                }
+                } break;
+
+                default:
+                    // Ignore all other extension structs
+                    break;
             }
         }
-
-        return result;
     }
+
+    return result;
 }
 
 VKAPI_ATTR
@@ -1783,6 +1799,173 @@
     return a != VK_SUCCESS ? a : b;
 }
 
+// KHR_incremental_present aspect of QueuePresentKHR
+static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) {
+    std::vector<android_native_rect_t> rects(pRegion->rectangleCount);
+    for (auto i = 0u; i < pRegion->rectangleCount; i++) {
+        auto const& rect = pRegion->pRectangles[i];
+        if (rect.layer > 0) {
+            ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead",
+                rect.layer);
+        }
+
+        rects[i].left = rect.offset.x;
+        rects[i].bottom = rect.offset.y;
+        rects[i].right = rect.offset.x + rect.extent.width;
+        rects[i].top = rect.offset.y + rect.extent.height;
+    }
+    native_window_set_surface_damage(window, rects.data(), rects.size());
+}
+
+// GOOGLE_display_timing aspect of QueuePresentKHR
+static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) {
+    ANativeWindow *window = swapchain.surface.window.get();
+
+    // We don't know whether the app will actually use GOOGLE_display_timing
+    // with a particular swapchain until QueuePresent; enable it on the BQ
+    // now if needed
+    if (!swapchain.frame_timestamps_enabled) {
+        ALOGV("Calling native_window_enable_frame_timestamps(true)");
+        native_window_enable_frame_timestamps(window, true);
+        swapchain.frame_timestamps_enabled = true;
+    }
+
+    // Record the nativeFrameId so it can be later correlated to
+    // this present.
+    uint64_t nativeFrameId = 0;
+    int err = native_window_get_next_frame_id(
+            window, &nativeFrameId);
+    if (err != android::OK) {
+        ALOGE("Failed to get next native frame ID.");
+    }
+
+    // Add a new timing record with the user's presentID and
+    // the nativeFrameId.
+    swapchain.timing.emplace_back(pTime, nativeFrameId);
+    if (swapchain.timing.size() > MAX_TIMING_INFOS) {
+        swapchain.timing.erase(
+            swapchain.timing.begin(),
+            swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS);
+    }
+    if (pTime->desiredPresentTime) {
+        ALOGV(
+            "Calling native_window_set_buffers_timestamp(%" PRId64 ")",
+            pTime->desiredPresentTime);
+        native_window_set_buffers_timestamp(
+            window,
+            static_cast<int64_t>(pTime->desiredPresentTime));
+    }
+}
+
+static VkResult PresentOneSwapchain(
+        VkQueue queue,
+        Swapchain& swapchain,
+        uint32_t imageIndex,
+        const VkPresentRegionKHR *pRegion,
+        const VkPresentTimeGOOGLE *pTime,
+        uint32_t waitSemaphoreCount,
+        const VkSemaphore *pWaitSemaphores) {
+
+    VkDevice device = GetData(queue).driver_device;
+    const auto& dispatch = GetData(queue).driver;
+
+    Swapchain::Image& img = swapchain.images[imageIndex];
+    VkResult swapchain_result = VK_SUCCESS;
+    VkResult result;
+    int err;
+
+    // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the
+    // wait semaphores, so this doesn't actually work for the multiple swapchain
+    // case.
+    int fence = -1;
+    result = dispatch.QueueSignalReleaseImageANDROID(
+        queue, waitSemaphoreCount,
+        pWaitSemaphores, img.image, &fence);
+    if (result != VK_SUCCESS) {
+        ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
+        swapchain_result = result;
+    }
+    if (img.release_fence >= 0)
+        close(img.release_fence);
+    img.release_fence = fence < 0 ? -1 : dup(fence);
+
+    if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) {
+        ANativeWindow* window = swapchain.surface.window.get();
+        if (swapchain_result == VK_SUCCESS) {
+
+            if (pRegion) {
+                SetSwapchainSurfaceDamage(window, pRegion);
+            }
+            if (pTime) {
+                SetSwapchainFrameTimestamp(swapchain, pTime);
+            }
+
+            err = window->queueBuffer(window, img.buffer.get(), fence);
+            // queueBuffer always closes fence, even on error
+            if (err != android::OK) {
+                ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+                swapchain_result = WorstPresentResult(
+                    swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+            } else {
+                if (img.dequeue_fence >= 0) {
+                    close(img.dequeue_fence);
+                    img.dequeue_fence = -1;
+                }
+                img.dequeued = false;
+            }
+
+            // If the swapchain is in shared mode, immediately dequeue the
+            // buffer so it can be presented again without an intervening
+            // call to AcquireNextImageKHR. We expect to get the same buffer
+            // back from every call to dequeueBuffer in this mode.
+            if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+                ANativeWindowBuffer* buffer;
+                int fence_fd;
+                err = window->dequeueBuffer(window, &buffer, &fence_fd);
+                if (err != android::OK) {
+                    ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+                    swapchain_result = WorstPresentResult(swapchain_result,
+                        VK_ERROR_SURFACE_LOST_KHR);
+                } else if (img.buffer != buffer) {
+                    ALOGE("got wrong image back for shared swapchain");
+                    swapchain_result = WorstPresentResult(swapchain_result,
+                        VK_ERROR_SURFACE_LOST_KHR);
+                } else {
+                    img.dequeue_fence = fence_fd;
+                    img.dequeued = true;
+                }
+            }
+        }
+        if (swapchain_result != VK_SUCCESS) {
+            OrphanSwapchain(device, &swapchain);
+        }
+        // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
+        // and only when the window's transform/rotation changes.  Extent
+        // changes will not cause VK_SUBOPTIMAL_KHR because of the
+        // application issues that were caused when the following transform
+        // change was added.
+        int window_transform_hint;
+        err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+                            &window_transform_hint);
+        if (err != android::OK) {
+            ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+                  strerror(-err), err);
+            swapchain_result = WorstPresentResult(
+                swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+        }
+        if (swapchain.pre_transform != window_transform_hint) {
+            swapchain_result =
+                WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+        }
+    } else {
+        ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
+                              img, true);
+        swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
+    }
+
+    return swapchain_result;
+}
+
 VKAPI_ATTR
 VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
     ATRACE_CALL();
@@ -1791,8 +1974,6 @@
              "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
              present_info->sType);
 
-    VkDevice device = GetData(queue).driver_device;
-    const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
 
     // Look at the pNext chain for supported extension structs:
@@ -1828,175 +2009,19 @@
         (present_regions) ? present_regions->pRegions : nullptr;
     const VkPresentTimeGOOGLE* times =
         (present_times) ? present_times->pTimes : nullptr;
-    const VkAllocationCallbacks* allocator = &GetData(device).allocator;
-    android_native_rect_t* rects = nullptr;
-    uint32_t nrects = 0;
 
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->pSwapchains[sc]);
-        uint32_t image_idx = present_info->pImageIndices[sc];
-        Swapchain::Image& img = swapchain.images[image_idx];
-        const VkPresentRegionKHR* region =
-            (regions && !swapchain.mailbox_mode) ? &regions[sc] : nullptr;
-        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
-        VkResult swapchain_result = VK_SUCCESS;
-        VkResult result;
-        int err;
 
-        int fence = -1;
-        result = dispatch.QueueSignalReleaseImageANDROID(
-            queue, present_info->waitSemaphoreCount,
-            present_info->pWaitSemaphores, img.image, &fence);
-        if (result != VK_SUCCESS) {
-            ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
-            swapchain_result = result;
-        }
-        if (img.release_fence >= 0)
-            close(img.release_fence);
-        img.release_fence = fence < 0 ? -1 : dup(fence);
-
-        if (swapchain.surface.swapchain_handle ==
-            present_info->pSwapchains[sc]) {
-            ANativeWindow* window = swapchain.surface.window.get();
-            if (swapchain_result == VK_SUCCESS) {
-                if (region) {
-                    // Process the incremental-present hint for this swapchain:
-                    uint32_t rcount = region->rectangleCount;
-                    if (rcount > nrects) {
-                        android_native_rect_t* new_rects =
-                            static_cast<android_native_rect_t*>(
-                                allocator->pfnReallocation(
-                                    allocator->pUserData, rects,
-                                    sizeof(android_native_rect_t) * rcount,
-                                    alignof(android_native_rect_t),
-                                    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
-                        if (new_rects) {
-                            rects = new_rects;
-                            nrects = rcount;
-                        } else {
-                            rcount = 0;  // Ignore the hint for this swapchain
-                        }
-                    }
-                    for (uint32_t r = 0; r < rcount; ++r) {
-                        if (region->pRectangles[r].layer > 0) {
-                            ALOGV(
-                                "vkQueuePresentKHR ignoring invalid layer "
-                                "(%u); using layer 0 instead",
-                                region->pRectangles[r].layer);
-                        }
-                        int x = region->pRectangles[r].offset.x;
-                        int y = region->pRectangles[r].offset.y;
-                        int width = static_cast<int>(
-                            region->pRectangles[r].extent.width);
-                        int height = static_cast<int>(
-                            region->pRectangles[r].extent.height);
-                        android_native_rect_t* cur_rect = &rects[r];
-                        cur_rect->left = x;
-                        cur_rect->top = y + height;
-                        cur_rect->right = x + width;
-                        cur_rect->bottom = y;
-                    }
-                    native_window_set_surface_damage(window, rects, rcount);
-                }
-                if (time) {
-                    if (!swapchain.frame_timestamps_enabled) {
-                        ALOGV(
-                            "Calling "
-                            "native_window_enable_frame_timestamps(true)");
-                        native_window_enable_frame_timestamps(window, true);
-                        swapchain.frame_timestamps_enabled = true;
-                    }
-
-                    // Record the nativeFrameId so it can be later correlated to
-                    // this present.
-                    uint64_t nativeFrameId = 0;
-                    err = native_window_get_next_frame_id(
-                            window, &nativeFrameId);
-                    if (err != android::OK) {
-                        ALOGE("Failed to get next native frame ID.");
-                    }
-
-                    // Add a new timing record with the user's presentID and
-                    // the nativeFrameId.
-                    swapchain.timing.emplace_back(time, nativeFrameId);
-                    while (swapchain.timing.size() > MAX_TIMING_INFOS) {
-                        swapchain.timing.erase(swapchain.timing.begin());
-                    }
-                    if (time->desiredPresentTime) {
-                        // Set the desiredPresentTime:
-                        ALOGV(
-                            "Calling "
-                            "native_window_set_buffers_timestamp(%" PRId64 ")",
-                            time->desiredPresentTime);
-                        native_window_set_buffers_timestamp(
-                            window,
-                            static_cast<int64_t>(time->desiredPresentTime));
-                    }
-                }
-
-                err = window->queueBuffer(window, img.buffer.get(), fence);
-                // queueBuffer always closes fence, even on error
-                if (err != android::OK) {
-                    ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
-                    swapchain_result = WorstPresentResult(
-                        swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
-                } else {
-                    if (img.dequeue_fence >= 0) {
-                        close(img.dequeue_fence);
-                        img.dequeue_fence = -1;
-                    }
-                    img.dequeued = false;
-                }
-
-                // If the swapchain is in shared mode, immediately dequeue the
-                // buffer so it can be presented again without an intervening
-                // call to AcquireNextImageKHR. We expect to get the same buffer
-                // back from every call to dequeueBuffer in this mode.
-                if (swapchain.shared && swapchain_result == VK_SUCCESS) {
-                    ANativeWindowBuffer* buffer;
-                    int fence_fd;
-                    err = window->dequeueBuffer(window, &buffer, &fence_fd);
-                    if (err != android::OK) {
-                        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
-                        swapchain_result = WorstPresentResult(swapchain_result,
-                            VK_ERROR_SURFACE_LOST_KHR);
-                    } else if (img.buffer != buffer) {
-                        ALOGE("got wrong image back for shared swapchain");
-                        swapchain_result = WorstPresentResult(swapchain_result,
-                            VK_ERROR_SURFACE_LOST_KHR);
-                    } else {
-                        img.dequeue_fence = fence_fd;
-                        img.dequeued = true;
-                    }
-                }
-            }
-            if (swapchain_result != VK_SUCCESS) {
-                OrphanSwapchain(device, &swapchain);
-            }
-            // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
-            // and only when the window's transform/rotation changes.  Extent
-            // changes will not cause VK_SUBOPTIMAL_KHR because of the
-            // application issues that were caused when the following transform
-            // change was added.
-            int window_transform_hint;
-            err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
-                                &window_transform_hint);
-            if (err != android::OK) {
-                ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
-                      strerror(-err), err);
-                swapchain_result = WorstPresentResult(
-                    swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
-            }
-            if (swapchain.pre_transform != window_transform_hint) {
-                swapchain_result =
-                    WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
-            }
-        } else {
-            ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
-                                  img, true);
-            swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
-        }
+        VkResult swapchain_result = PresentOneSwapchain(
+            queue,
+            swapchain,
+            present_info->pImageIndices[sc],
+            (regions && !swapchain.mailbox_mode) ? &regions[sc] : nullptr,
+            times ? &times[sc] : nullptr,
+            present_info->waitSemaphoreCount,
+            present_info->pWaitSemaphores);
 
         if (present_info->pResults)
             present_info->pResults[sc] = swapchain_result;
@@ -2004,9 +2029,6 @@
         if (swapchain_result != final_result)
             final_result = WorstPresentResult(final_result, swapchain_result);
     }
-    if (rects) {
-        allocator->pfnFree(allocator->pUserData, rects);
-    }
 
     return final_result;
 }